suppressPackageStartupMessages(library("ggplot2"))
theme_set(ggpubr::theme_pubr(base_size=10, legend='bottom'))
suppressPackageStartupMessages(library("DESeq2"))

#Note that we do not have Clark Scores or MIA scores for AVAST-M lymph node samples and these need to be commented out to avoid errors.

#Load the data
tissue<-c("Skin")
load(paste("~/Desktop/Melanoma/deseq2Results/tc",tissue,"EventMetNo_VS_tc",tissue,"EventMetYes_CovariateCorrection.deseq2/de.Rdata", sep=""))
#find ENSEMBL ID corresponding to GRAMD1B
geneName<-"GRAMD1B"
select<-as.character(res.annot$id[res.annot$Name==geneName])
#extract expression of this gene
expressionData <- data.frame(assay(vsd)[select, ])
#Replace ENSEMBL IDs with corresponding gene names
names(expressionData) <- geneName
stand.fun <- function(x){(x-mean(x,na.rm=TRUE))/sd(x,na.rm=TRUE)}
stdSignature<-stand.fun(expressionData$GRAMD1B)
names(stdSignature)<-rownames(expressionData) #make sure they retain the name of the samples
#Multiply by beta coefficient?
#betaCoeff <- read.table("~/Desktop/Melanoma/betaCoeff.tsv", sep = "\t", header = TRUE, quote = "")
#extract beta coefficient for the gene of interest
#betaCoeffGene <- betaCoeff[select, "EventMet_Yes_vs_No"]
#weightedSignature <- expressionData*betaCoeffGene
#standardization of the weighted signature
#stand.fun <- function(x){(x-mean(x,na.rm=TRUE))/sd(x,na.rm=TRUE)}
#stdWeightedSignature <- stand.fun(weightedSignature$GRAMD1B)
#names(stdWeightedSignature)<-rownames(weightedSignature) #make sure they retain the name of the samples

As expected the absolute values after standardization of weighted signature (stand.fun(weightedSignature$GRAMD1B)) is same as those of standardized expression values (stand.fun(expressionData$GRAMD1B)). The only difference comes from sign of the beta coefficient which is negative for GRAMD1B

#Load clinical data
clinicalData<-data.frame(colData(vsd))
clinicalData$Signature<- stdSignature[rownames(clinicalData)] #merge signature in clinical data frame

#Check association of GRAMD1B with relapse

my_comparisons <- list( c("Yes", "No"))
ggplot(clinicalData[!is.na(clinicalData$EventMet),], aes(x=EventMet, y=Signature))+
  geom_violin()+
  geom_jitter(height = 0, width = 0.1, alpha = 0.5, aes())+
  #scale_fill_brewer(type="qual", palette = "Dark2", name = "Cam_121 risk group")+
  xlab("Distant metastases (AVAST-M Skin)")+
  ylab("VST normalized GRAMD1B expression (stand.)")+
  #geom_hline(yintercept = quantile(clinicalData$Signature, 0.75), color = "grey", linetype = "longdash")+
  theme(text=element_text(size=7,  family="sans"))+
  ggpubr::stat_compare_means(comparisons = my_comparisons, method = "t.test", size = 2.5, family="sans")#+ # Add pairwise comparisons p-value
  #ggpubr::stat_compare_means(label.y = 6, method = "anova", size = 2.5, family="sans")#+

Find suitable cut-off. After talking to Roy, it seems that we could go for a weighted GRAMD1B value of 0? Let’s check the median value. May be I can take the median cut-off and perform chi-squared test to check if there is no difference between the mean of 2 groups?

#Divide in high/low groups based on mean and median
co_mean<-mean(clinicalData$Signature)
co_median<-median(clinicalData$Signature)
co_0_33<-quantile(clinicalData$Signature, 0.33)
co_0_66<-quantile(clinicalData$Signature, 0.66)
if (tissue=="Skin") {
  co_density_intersection<--0.223156179#0.235202939
}else{
  co_density_intersection<-0.3481057115#-0.223156179#0.235202939
}
suppressPackageStartupMessages(library(plotly))
p<-ggplot(data = clinicalData, aes(x = Signature, fill=EventMet)) +
  geom_density(alpha=0.3)+
  geom_vline(xintercept=c(co_mean, co_median, co_0_33, co_0_66, co_density_intersection), linetype=c('twodash', 'longdash', 'dotted', 'dotdash', 'dashed'))+
  ggtitle(tissue)
ggplotly(p) 
clinicalData$SignatureGroupMean <- as.factor(ifelse(clinicalData$Signature >= as.numeric(co_mean), "High", "Low"))
clinicalData$SignatureGroupMedian <- as.factor(ifelse(clinicalData$Signature >= as.numeric(co_median), "High", "Low"))
clinicalData$SignatureGroup0_33 <- as.factor(ifelse(clinicalData$Signature >= as.numeric(co_0_33), "High", "Low"))
clinicalData$SignatureGroup0_66 <- as.factor(ifelse(clinicalData$Signature >= as.numeric(co_0_66), "High", "Low"))
clinicalData$SignatureGroupDensityIntersection <- as.factor(ifelse(clinicalData$Signature >= as.numeric(co_density_intersection), "High", "Low"))

print(chisq.test(clinicalData$SignatureGroupMean, clinicalData$EventMet))
print(chisq.test(clinicalData$SignatureGroupMedian, clinicalData$EventMet))
print(chisq.test(clinicalData$SignatureGroup0_33, clinicalData$EventMet))
print(chisq.test(clinicalData$SignatureGroup0_66, clinicalData$EventMet))
print(chisq.test(clinicalData$SignatureGroupDensityIntersection, clinicalData$EventMet))

#Check association of NRAS with other clinical covariates

print(chisq.test(clinicalData$NRAS, clinicalData$EventMet))
print(chisq.test(clinicalData$NRAS, clinicalData$Bres))
print(chisq.test(clinicalData$NRAS, clinicalData$Ulc))

#Check association of GRAMD1B with ulceration

my_comparisons <- list( c("Present", "Absent"))
ggplot(clinicalData[!is.na(clinicalData$Ulc),], aes(x=Ulc, y=Signature))+
  geom_violin()+
  geom_jitter(height = 0, width = 0.1, alpha = 0.5, aes())+
  #scale_fill_brewer(type="qual", palette = "Dark2", name = "Cam_121 risk group")+
  xlab("Ulc (AVAST-M Skin)")+
  ylab("VST normalized GRAMD1B expression (stand.)")+
  #geom_hline(yintercept = quantile(clinicalData$Signature, 0.75), color = "grey", linetype = "longdash")+
  theme(text=element_text(size=7,  family="sans"))+
  ggpubr::stat_compare_means(comparisons = my_comparisons, method = "t.test", size = 2.5, family="sans")#+ # Add pairwise comparisons p-value
  #ggpubr::stat_compare_means(label.y = 6, method = "anova", size = 2.5, family="sans")#+

#Check association of GRAMD1B with Bres

my_comparisons <- list( c("<= 2.0 mm", ">2-4mm"), c("<= 2.0 mm", ">4.0mm"), c(">2-4mm", ">4.0mm"))
ggplot(clinicalData[!is.na(clinicalData$Bres),], aes(x=Bres, y=Signature))+
  geom_violin()+
  geom_jitter(height = 0, width = 0.1, alpha = 0.5, aes())+
  #scale_fill_brewer(type="qual", palette = "Dark2", name = "Cam_121 risk group")+
  xlab("Bres (AVAST-M Skin)")+
  ylab("VST normalized GRAMD1B expression (stand.)")+
  #geom_hline(yintercept = quantile(clinicalData$Signature, 0.75), color = "grey", linetype = "longdash")+
  theme(text=element_text(size=7,  family="sans"))+
  ggpubr::stat_compare_means(comparisons = my_comparisons, method = "t.test", size = 2.5, family="sans")+ # Add pairwise comparisons p-value
  ggpubr::stat_compare_means(label.y = 6, method = "anova", size = 2.5, family="sans")#+

#Check association of GRAMD1B with Stage

my_comparisons <- list( c("IIB", "IIC"), c("IIB", "IIIA"), c("IIB", "IIIB"), c("IIB", "IIIC"),
                        c("IIC", "IIIA"), c("IIC", "IIIB"), c("IIC", "IIIC"),
                        c("IIIA", "IIIB"), c("IIIA", "IIIC"),
                        c("IIIB", "IIIC"))
ggplot(clinicalData[!is.na(clinicalData$Stage),], aes(x=Stage, y=Signature))+
  geom_violin()+
  geom_jitter(height = 0, width = 0.1, alpha = 0.5, aes())+
  #scale_fill_brewer(type="qual", palette = "Dark2", name = "Cam_121 risk group")+
  xlab("Stage (AVAST-M Skin)")+
  ylab("VST normalized GRAMD1B expression (stand.)")+
  #geom_hline(yintercept = quantile(clinicalData$Signature, 0.75), color = "grey", linetype = "longdash")+
  theme(text=element_text(size=7,  family="sans"))+
  ggpubr::stat_compare_means(comparisons = my_comparisons, method = "t.test", size = 2.5, family="sans")+ # Add pairwise comparisons p-value
  ggpubr::stat_compare_means(label.y = 9, method = "anova", size = 2.5, family="sans")#+
ggplot(clinicalData[!is.na(clinicalData$Stage_binary),], aes(x=Stage_binary, y=Signature))+
  geom_violin()+
  geom_jitter(height = 0, width = 0.1, alpha = 0.5, aes())+
  #scale_fill_brewer(type="qual", palette = "Dark2", name = "Cam_121 risk group")+
  xlab("Stage (AVAST-M Skin)")+
  ylab("VST normalized GRAMD1B expression (stand.)")+
  #geom_hline(yintercept = quantile(clinicalData$Signature, 0.75), color = "grey", linetype = "longdash")+
  theme(text=element_text(size=7,  family="sans"))+
  ggpubr::stat_compare_means(method = "t.test", size = 2.5, family="sans")#+ # Add pairwise comparisons p-value
  #ggpubr::stat_compare_means(label.y = 9, method = "anova", size = 2.5, family="sans")

#Check association of GRAMD1B with Site

my_comparisons <- list( c("Head and neck", "Lower lims"), c("Head and neck", "Trunk"), c("Head and neck", "Upper limbs"),
                        c("Lower lims", "Trunk"), c("Lower lims", "Upper limbs"),
                        c("Trunk", "Upper limbs"))
ggplot(clinicalData[!is.na(clinicalData$Site),], aes(x=Site, y=Signature))+
  geom_violin()+
  geom_jitter(height = 0, width = 0.1, alpha = 0.5, aes())+
  #scale_fill_brewer(type="qual", palette = "Dark2", name = "Cam_121 risk group")+
  xlab("Site (AVAST-M Skin)")+
  ylab("VST normalized GRAMD1B expression (stand.)")+
  #geom_hline(yintercept = quantile(clinicalData$Signature, 0.75), color = "grey", linetype = "longdash")+
  theme(text=element_text(size=7,  family="sans"))+
  ggpubr::stat_compare_means(comparisons = my_comparisons, method = "t.test", size = 2.5, family="sans")+ # Add pairwise comparisons p-value
  ggpubr::stat_compare_means(label.y = 9, method = "anova", size = 2.5, family="sans")#+

#Check association of GRAMD1B with BRAF mutation

my_comparisons <- list( c("V600E", "WT"))
ggplot(clinicalData[!is.na(clinicalData$BRAF),], aes(x=BRAF, y=Signature))+
  geom_violin()+
  geom_jitter(height = 0, width = 0.1, alpha = 0.5, aes())+
  #scale_fill_brewer(type="qual", palette = "Dark2", name = "Cam_121 risk group")+
  xlab("BRAF (AVAST-M Skin)")+
  ylab("VST normalized GRAMD1B expression (stand.)")+
  #geom_hline(yintercept = quantile(clinicalData$Signature, 0.75), color = "grey", linetype = "longdash")+
  theme(text=element_text(size=7,  family="sans"))+
  ggpubr::stat_compare_means(comparisons = my_comparisons, method = "t.test", size = 2.5, family="sans")#+ # Add pairwise comparisons p-value
  #ggpubr::stat_compare_means(label.y = 9, method = "anova", size = 2.5, family="sans")#+

#Check association of GRAMD1B with NRAS mutation

my_comparisons <- list( c("Mutant", "WT"))
ggplot(clinicalData[!is.na(clinicalData$NRAS),], aes(x=NRAS, y=Signature))+
  geom_violin()+
  geom_jitter(height = 0, width = 0.1, alpha = 0.5, aes())+
  #scale_fill_brewer(type="qual", palette = "Dark2", name = "Cam_121 risk group")+
  xlab("NRAS (AVAST-M Skin)")+
  ylab("VST normalized GRAMD1B expression (stand.)")+
  #geom_hline(yintercept = quantile(clinicalData$Signature, 0.75), color = "grey", linetype = "longdash")+
  theme(text=element_text(size=7,  family="sans"))+
  ggpubr::stat_compare_means(comparisons = my_comparisons, method = "t.test", size = 2.5, family="sans")#+ # Add pairwise comparisons p-value
  #ggpubr::stat_compare_means(label.y = 9, method = "anova", size = 2.5, family="sans")#+

#Check association of GRAMD1B with TIL counts

jt_ns_sm_scores = read.table("../../../JT_NS_SM_Combined.xls - JT_NS_SM.tsv", sep = "\t", header = TRUE, quote = "")
combinedScore <- data.frame("Scanned_file.me"= jt_ns_sm_scores$JT_Scanned_file.me, 
                                "R.Seq_sampleID"= jt_ns_sm_scores$JT_R.Seq_sampleID, 
                                "ClarkScore"= ifelse(jt_ns_sm_scores$JT_Clark_score==jt_ns_sm_scores$NS_Clark_score,
                            jt_ns_sm_scores$JT_Clark_score, 
                            jt_ns_sm_scores$SM_Clark_score),
                           "MIAScore"= ifelse(jt_ns_sm_scores$JT_TIL_grade==jt_ns_sm_scores$NS_TIL.GRADE,
                            jt_ns_sm_scores$JT_TIL_grade, 
                            jt_ns_sm_scores$SM_TIL_grade))
#Remove duplicated entries
combinedScore<-combinedScore[!duplicated(combinedScore$R.Seq_sampleID), ]
rownames(combinedScore)<-combinedScore$R.Seq_sampleID #rename rownames with sample ids
rownames(clinicalData)<-clinicalData$RNA.Seq.Sample
df<-data.frame("EventMet"=clinicalData$EventMet,
               "ClarkScore"=combinedScore[rownames(clinicalData), "ClarkScore"],
               "MIAScore"=combinedScore[rownames(clinicalData), "MIAScore"],
               "Signature"=clinicalData$Signature)
rownames(df)<-rownames(clinicalData)

df$ClarkScore <- as.factor(df$ClarkScore)
levels(df$ClarkScore) <- c("absent", "nonbrisk", "brisk")
my_comparisons <- list( c("absent", "nonbrisk"), c("nonbrisk", "brisk"), c("absent", "brisk") )
#df$JT_Clark_score <- factor(df$JT_Clark_score, levels = c("absent", "nonbrisk", "brisk"))
ggplot(df[!is.na(df$ClarkScore), ], aes(x=ClarkScore, y=Signature#, color=DRBrain
                                        ))+
  geom_violin()+
  geom_jitter(height = 0, width = 0.1, alpha = 0.5, aes())+
  #scale_fill_brewer(type="qual", palette = "Dark2", name = "Cam_121 risk group")+
  xlab("Clark score (AVAST-M Skin)")+
  ylab("VST normalized GRAMD1B expression (stand.)")+
  #geom_hline(yintercept = quantile(df$Signature, 0.75), color = "grey", linetype = "longdash")+
  theme(text=element_text(size=7,  family="sans"))+
  ggpubr::stat_compare_means(comparisons = my_comparisons, method = "t.test", size = 2.5, family="sans")+ # Add pairwise comparisons p-value
  ggpubr::stat_compare_means(label.y = 6, method = "anova", size = 2.5, family="sans")#+
 # facet_wrap(~DRBrain)# Add global p-value
my_comparisons <- list( c("0", "1"), c("0", "2"), c("0", "3"), c("1", "2"), c("1", "3"), c("2", "3"))
df$MIAScore <- as.factor(df$MIAScore)
ggplot(df[!is.na(df$MIAScore), ], aes(x=MIAScore, y=Signature#, color=DRBrain
                                      ))+
  geom_violin()+
  geom_jitter(height = 0, width = 0.1, alpha = 0.5)+
  scale_fill_brewer(type="qual", palette = "Dark2", name = "MIA score")+
  xlab("MIA score (AVAST-M Skin)")+
  ylab("VST normalized GRAMD1B expression (stand.)")+
  #geom_hline(yintercept = quantile(df$Signature, 0.75), color = "grey", linetype = "longdash")+
  theme(text=element_text(size=7,  family="sans"))+
  ggpubr::stat_compare_means(comparisons = my_comparisons, method = "t.test", size = 2.5, family="sans")+ # Add pairwise comparisons p-value
  ggpubr::stat_compare_means(label.y = 6, method = "anova", size = 2.5, family="sans")#+
  #facet_wrap(~DRBrain)
     # Add global p-value
#my_comparisons <- list( c("Mutant", "WT"))
ggplot(clinicalData[!is.na(clinicalData$treatment),], aes(x=treatment, y=Signature))+
  geom_violin()+
  geom_jitter(height = 0, width = 0.1, alpha = 0.5, aes())+
  #scale_fill_brewer(type="qual", palette = "Dark2", name = "Cam_121 risk group")+
  xlab("Treatment (AVAST-M Skin)")+
  ylab("VST normalized GRAMD1B expression (stand.)")+
  #geom_hline(yintercept = quantile(clinicalData$Signature, 0.75), color = "grey", linetype = "longdash")+
  theme(text=element_text(size=7,  family="sans"))+
  ggpubr::stat_compare_means(method = "t.test", size = 2.5, family="sans")#+ # Add pairwise comparisons p-value
  #ggpubr::stat_compare_means(label.y = 9, method = "anova", size = 2.5, family="sans")#+
#my_comparisons <- list( c("Mutant", "WT"))
ggplot(clinicalData[!is.na(clinicalData$ECOG),], aes(x=ECOG, y=Signature))+
  geom_violin()+
  geom_jitter(height = 0, width = 0.1, alpha = 0.5, aes())+
  #scale_fill_brewer(type="qual", palette = "Dark2", name = "Cam_121 risk group")+
  xlab("ECOG (AVAST-M Skin)")+
  ylab("VST normalized GRAMD1B expression (stand.)")+
  #geom_hline(yintercept = quantile(clinicalData$Signature, 0.75), color = "grey", linetype = "longdash")+
  theme(text=element_text(size=7,  family="sans"))+
  ggpubr::stat_compare_means(method = "t.test", size = 2.5, family="sans")#+ # Add pairwise comparisons p-value
  #ggpubr::stat_compare_means(label.y = 9, method = "anova", size = 2.5, family="sans")#+

#Check the trend of NFKB pathway with GRAMD1B signature

HALLMARK_TNFA_SIGNALING_VIA_NFKB_genes<- c('JUNB','CXCL2','ATF3','NFKBIA','TNFAIP3','PTGS2','CXCL1','IER3','CD83','CCL20','CXCL3','MAFF','NFKB2','TNFAIP2','HBEGF','KLF6','BIRC3','PLAUR','ZFP36','ICAM1','JUN','EGR3','IL1B','BCL2A1','PPP1R15A','ZC3H12A','SOD2','NR4A2','IL1A','RELB','TRAF1','BTG2','DUSP1','MAP3K8','ETS2','F3','SDC4','EGR1','IL6','TNF','KDM6B','NFKB1','LIF','PTX3','FOSL1','NR4A1','JAG1','CCL4','GCH1','CCL2','RCAN1','DUSP2','EHD1','IER2','REL','CFLAR','RIPK2','NFKBIE','NR4A3','PHLDA1','IER5','TNFSF9','GEM','GADD45A','CXCL10','PLK2','BHLHE40','EGR2','SOCS3','SLC2A6','PTGER4','DUSP5','SERPINB2','NFIL3','SERPINE1','TRIB1','TIPARP','RELA','BIRC2','CXCL6','LITAF','TNFAIP6','CD44','INHBA','PLAU','MYC','TNFRSF9','SGK1','TNIP1','NAMPT','FOSL2','PNRC1','ID2','CD69','IL7R','EFNA1','PHLDA2','PFKFB3','CCL5','YRDC','IFNGR2','SQSTM1','BTG3','GADD45B','KYNU','G0S2','BTG1','MCL1','VEGFA','MAP2K3','CDKN1A','TANK','IFIT2','IL18','TUBB2A','IRF1','FOS','OLR1','RHOB','AREG','NINJ1','ZBTB10','PLPP3','KLF4','CXCL11','SAT1','CSF1','GPR183','PMEPA1','PTPRE','TLR2','ACKR3','KLF10','MARCKS','LAMB3','CEBPB','TRIP10','F2RL1','KLF9','LDLR','TGIF1','RNF19B','DRAM1','B4GALT1','DNAJB4','CSF2','PDE4B','SNN','PLEK','STAT5A','DENND5A','CCND1','DDX58','SPHK1','CD80','TNFAIP8','CCNL1','FUT4','CCRL2','SPSB1','TSC22D1','B4GALT5','SIK1','CLCF1','NFE2L2','FOSB','PER1','NFAT5','ATP2B1','IL12B','IL6ST','SLC16A6','ABCA1','HES1','BCL6','IRS2','SLC2A3','CEBPD','IL23A','SMAD3','TAP1','MSC','IFIH1','IL15RA','TNIP2','BCL3','PANX1','FJX1','EDN1','EIF1','BMP2','DUSP4','PDLIM5','ICOSLG','GFPT2','KLF2','TNC','SERPINB8','MXD1')
ensembl_ids<-res.annot$id[res.annot$Name%in% HALLMARK_TNFA_SIGNALING_VIA_NFKB_genes]
expressionDataForHallmark<-data.frame(assay(vsd)[ensembl_ids, ])
#Calculate average value per sample
medianCellTypeExpression = apply(t(expressionDataForHallmark), 1, median)
#Replace ENSEMBL IDs with corresponding gene names
#names(expressionData) <- geneName
stand.fun <- function(x){(x-mean(x,na.rm=TRUE))/sd(x,na.rm=TRUE)}
clinicalData$HALLMARK_TNFA_SIGNALING_VIA_NFKB_genes<-stand.fun(medianCellTypeExpression)[rownames(clinicalData)]
clinicalData$HALLMARK_TNFA_SIGNALING_VIA_NFKB_genes_groups<-ifelse(clinicalData$HALLMARK_TNFA_SIGNALING_VIA_NFKB_genes >= median(clinicalData$HALLMARK_TNFA_SIGNALING_VIA_NFKB_genes), "High", "Low")
#plot the results 
ggplot(clinicalData, aes(x=SignatureGroupMean, y=HALLMARK_TNFA_SIGNALING_VIA_NFKB_genes))+
  geom_violin()+
  geom_jitter(height = 0, width = 0.1, alpha = 0.5, aes())+
  #scale_fill_brewer(type="qual", palette = "Dark2", name = "Cam_121 risk group")+
  xlab("GRAMD1B groups")+
  ylab("HALLMARK_TNFA_SIGNALING_VIA_NFKB\n(Stand. median VST normalized gene expression)")+
  #geom_hline(yintercept = quantile(clinicalData$Signature, 0.75), color = "grey", linetype = "longdash")+
  theme(text=element_text(size=7,  family="sans"))+
  geom_hline(yintercept = 0, linetype="dashed")+
  ggpubr::stat_compare_means(method = "t.test", size = 2.5, family="sans")#+ # Add pairwise comparisons p-value

  #ggpubr::stat_compare_means(label.y = 9, method = "anova", size = 2.5, family="sans")#+
plot_ly(x=df$PC1, y=df$PC2, z=df$PC3, type="scatter3d", mode="markers", color=df$GRAMD1B_groups, symbols =df$HALLMARK_groups)
Warning in RColorBrewer::brewer.pal(N, "Set2") :
  minimal value for n is 3, returning requested palette with 3 different levels

Warning in RColorBrewer::brewer.pal(N, "Set2") :
  minimal value for n is 3, returning requested palette with 3 different levels

Warning in RColorBrewer::brewer.pal(N, "Set2") :
  minimal value for n is 3, returning requested palette with 3 different levels

Warning in RColorBrewer::brewer.pal(N, "Set2") :
  minimal value for n is 3, returning requested palette with 3 different levels

#Perform differential expression analysis

combinedScore$ClarkScore <- as.factor(combinedScore$ClarkScore)
levels(combinedScore$ClarkScore) <- c("absent", "nonbrisk", "brisk")
clinicalData$ClarkScore<-combinedScore[rownames(clinicalData), "ClarkScore"]
#merge(clinicalData, combinedScore, by.x="RNA.Seq.Sample", by.y="R.Seq_sampleID")
#rownames(clinicalData)<-clinicalData$RNA.Seq.Sample
#clinicalData$ClarkScore=combinedScore$ClarkScore[rownames(clinicalData)]
#While accounting for Stage, NRAS mutation, TIL score and EventMet
#Subset to only those samples which have non-missing entires for these covariates
clinicalData_sub<-clinicalData[(!is.na(clinicalData$Stage))&(!is.na(clinicalData$NRAS))&(!is.na(clinicalData$ClarkScore))&(!is.na(clinicalData$EventMet)), ]
data.f_sub<-data.f[, rownames(clinicalData_sub)]
table(clinicalData_sub$SignatureGroupMean)
dds1 <- DESeqDataSetFromMatrix(countData = data.f_sub,
                               colData   = clinicalData_sub,
                               design    = ~ Stage+NRAS+ClarkScore+EventMet+SignatureGroupMean)
totalcounts1.gene = rowSums(counts(dds1))
dds1 <- dds1[totalcounts1.gene>=10,]
# dimensions post filtering
m = nrow(dds1)
n = ncol(dds1)
dds1 <- DESeq(dds1)
res1 <- results(dds1)
res1
#Merge with gene names and check if the differentially expressed genes make sense
res<-data.frame(res1)
res<-res[order(res$padj), ]
res$Name<-res.annot[rownames(res), "Name"]
head(res)
#GRAMD1B is differentially expressed in GRAMD1B groups and is downregulated in the low expression group compared to the high-expression group so atleast this makes sense.

#Perform lfc shrinkage for GSEA

library("apeglm")
coefName = resultsNames(dds1)
resWithLfcShrinkage = data.frame(lfcShrink(dds1, coef = tail(coefName, n=1), type="apeglm"))
#Add suffix to colnames for easy identification later on.
colnames(resWithLfcShrinkage) = paste(colnames(resWithLfcShrinkage),"lfcShrinkApplied", sep = "_")
#Add ENSEMBL ID as a column in the data frame to ease merging later
resWithLfcShrinkage$id = rownames(resWithLfcShrinkage)
#Merge these with DE original DE results
res$id<-rownames(res)
deResultsUpdated = merge(x = res, y = data.frame(resWithLfcShrinkage), by = "id")
deResultsUpdated = deResultsUpdated[order(deResultsUpdated$padj),]
#Save the results
write.table(deResultsUpdated, file = "../results/de_gsea/deWithLfcSkrinkageApplied_GRAMD1B_all.tsv", col.names = TRUE, row.names = FALSE, quote = FALSE, sep = '\t')
#Save the differentially expressed genes (padj<0.1)
write.table(deResultsUpdated[deResultsUpdated$padj_lfcShrinkApplied<0.1, ], file = "../results/de_gsea/deWithLfcSkrinkageApplied_GRAMD1B_padj_0_1.tsv", col.names = TRUE, row.names = FALSE, quote = FALSE, sep = '\t')
head(deResultsUpdated)

#Create a ranked list for GSEA

#Create ranked list for running Pre-ranked GSEA tool
deResultsUpdated<-read.table("../results/de_gsea/deWithLfcSkrinkageApplied_GRAMD1B_all.tsv", header = T, sep = "\t", quote = "")
rankedList = na.omit(data.frame(deResultsUpdated$Name, deResultsUpdated$log2FoldChange_lfcShrinkApplied))
rankedList = rankedList[with(rankedList, order(deResultsUpdated.log2FoldChange_lfcShrinkApplied, decreasing = T)), ]
#Save the ranked list to a new file for GSEA.
write.table(rankedList, file = "../results/de_gsea/rankedList_GRAMD1B.rnk", col.names = FALSE, row.names = FALSE, quote = FALSE, sep = '\t')

#Plot survival curves between the continuous signature and the two groups ## First add necesssary columns

suppressPackageStartupMessages(library("survival"))
# survival outcome 1: "d" for death and "ltrc" for left truncated and right censored
clinicalData$survival_d_ltrc = Surv(time  = as.numeric(difftime(clinicalData$DOE, clinicalData$DDiag))/365.25,
                                    time2 = as.numeric(difftime(clinicalData$DOC, clinicalData$DDiag))/365.25,
                                    event = ifelse(clinicalData$Dead == FALSE, 0, 1))

# survival outcome 2: "rd" for relapse or death, "ltrc" for left truncated and right censored
clinicalData$survival_rd_ltrc = Surv(time  = as.numeric(difftime(clinicalData$DOE, clinicalData$DDiag))/365.25,
                                     time2 = as.numeric(difftime(apply(clinicalData[, c("DOC", "DDistMets")],1,min,na.rm=TRUE), clinicalData$DDiag))/365.25,
                                     event = ifelse((clinicalData$Dead == FALSE & clinicalData$EventMet == "No"), 0, 1))

if(tissue=="Skin"){
  jt_ns_sm_scores = read.table("../JT_NS_SM_Combined.xls - JT_NS_SM.tsv", sep = "\t", header = TRUE, quote = "")
  combinedScore <- data.frame("Scanned_file.me"= jt_ns_sm_scores$JT_Scanned_file.me, 
                              "R.Seq_sampleID"= jt_ns_sm_scores$JT_R.Seq_sampleID, 
                              "ClarkScore"=ifelse(jt_ns_sm_scores$JT_Clark_score==jt_ns_sm_scores$NS_Clark_score,
                                                  jt_ns_sm_scores$JT_Clark_score, 
                                                  jt_ns_sm_scores$SM_Clark_score),
                           "MIAScore"= ifelse(jt_ns_sm_scores$JT_TIL_grade==jt_ns_sm_scores$NS_TIL.GRADE,
                                              jt_ns_sm_scores$JT_TIL_grade, 
                                              jt_ns_sm_scores$SM_TIL_grade))
  #Remove duplicated entries
  combinedScore<-combinedScore[!duplicated(combinedScore$R.Seq_sampleID), ]
  rownames(combinedScore)<-combinedScore$R.Seq_sampleID #rename rownames with sample ids
  combinedScore$ClarkScore <- as.factor(combinedScore$ClarkScore)
  levels(combinedScore$ClarkScore) <- c("absent", "nonbrisk", "brisk")
  clinicalData<-merge(clinicalData, combinedScore, by.x="RNA.Seq.Sample", by.y="R.Seq_sampleID")
  rownames(clinicalData)<-clinicalData$RNA.Seq.Sample
}

then calculate the values

os<-data.frame()
pfs<-data.frame()
temp<-data.frame()

combinations<-c("Signature", "SignatureGroupMean", "SignatureGroupMedian", "SignatureGroup0_33", "SignatureGroup0_66", "SignatureGroupDensityIntersection")

for (combination in combinations) {
  if (tissue=="Skin") {
    os_formula<-paste0("survival_d_ltrc~",combination,"+Sex+Age+as.numeric(Nclass)+as.character(Stage)+ECOG+treatment+ClarkScore")
    pfs_formula<-paste0("survival_rd_ltrc~",combination,"+Sex+Age+as.numeric(Nclass)+as.character(Stage)+ECOG+treatment+ClarkScore")
  } else{
    os_formula<-paste0("survival_d_ltrc~",combination,"+Sex+Age+as.numeric(Nclass)+as.character(Stage)+ECOG+treatment")
    pfs_formula<-paste0("survival_rd_ltrc~",combination,"+Sex+Age+as.numeric(Nclass)+as.character(Stage)+ECOG+treatment")
  }
  
  fit = coef(summary(coxph(as.formula(os_formula), data=clinicalData)))
  mid  = fit[1,c("exp(coef)")]
  low  = exp(fit[1,c("coef")]-
              qnorm(.975)*fit[1,c("se(coef)")])                   
  high = exp(fit[1,c("coef")]+
              qnorm(.975)*fit[1,c("se(coef)")])  
  pval = fit[1,c("Pr(>|z|)")]
  temp["Signature",c("Signature", "HR","low","high","pval")] = c(rownames(fit)[1],mid,low,high,pval)
  os<-rbind(os, temp)
  
  fit = coef(summary(coxph(as.formula(pfs_formula), data=clinicalData)))
  mid  = fit[1,c("exp(coef)")]
  low  = exp(fit[1,c("coef")]-
              qnorm(.975)*fit[1,c("se(coef)")])                   
  high = exp(fit[1,c("coef")]+
              qnorm(.975)*fit[1,c("se(coef)")])  
  pval = fit[1,c("Pr(>|z|)")]
  temp["Signature",c("Signature", "HR","low","high","pval")] = c(rownames(fit)[1],mid,low,high,pval)
  pfs<-rbind(pfs, temp)
}

write.table(os, paste("../results/survival/AVAST-M_",tissue,"_os.tsv", sep=""), sep="\t", col.names = T, row.names = F)
write.table(pfs, paste("../results/survival/AVAST-M_",tissue,"_pfs.tsv", sep=""), sep="\t", col.names = T, row.names = F)
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3J9CnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyhsaWJyYXJ5KCJnZ3Bsb3QyIikpCnRoZW1lX3NldChnZ3B1YnI6OnRoZW1lX3B1YnIoYmFzZV9zaXplPTEwLCBsZWdlbmQ9J2JvdHRvbScpKQpgYGAKCmBgYHtyfQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeSgiREVTZXEyIikpCmBgYAoKI05vdGUgdGhhdCB3ZSBkbyBub3QgaGF2ZSBDbGFyayBTY29yZXMgb3IgTUlBIHNjb3JlcyBmb3IgQVZBU1QtTSBseW1waCBub2RlIHNhbXBsZXMgYW5kIHRoZXNlIG5lZWQgdG8gYmUgY29tbWVudGVkIG91dCB0byBhdm9pZCBlcnJvcnMuIApgYGB7cn0KI0xvYWQgdGhlIGRhdGEKdGlzc3VlPC1jKCJTa2luIikKbG9hZChwYXN0ZSgifi9EZXNrdG9wL01lbGFub21hL2Rlc2VxMlJlc3VsdHMvdGMiLHRpc3N1ZSwiRXZlbnRNZXROb19WU190YyIsdGlzc3VlLCJFdmVudE1ldFllc19Db3ZhcmlhdGVDb3JyZWN0aW9uLmRlc2VxMi9kZS5SZGF0YSIsIHNlcD0iIikpCmBgYAoKYGBge3J9CiNmaW5kIEVOU0VNQkwgSUQgY29ycmVzcG9uZGluZyB0byBHUkFNRDFCCmdlbmVOYW1lPC0iR1JBTUQxQiIKc2VsZWN0PC1hcy5jaGFyYWN0ZXIocmVzLmFubm90JGlkW3Jlcy5hbm5vdCROYW1lPT1nZW5lTmFtZV0pCiNleHRyYWN0IGV4cHJlc3Npb24gb2YgdGhpcyBnZW5lCmV4cHJlc3Npb25EYXRhIDwtIGRhdGEuZnJhbWUoYXNzYXkodnNkKVtzZWxlY3QsIF0pCiNSZXBsYWNlIEVOU0VNQkwgSURzIHdpdGggY29ycmVzcG9uZGluZyBnZW5lIG5hbWVzCm5hbWVzKGV4cHJlc3Npb25EYXRhKSA8LSBnZW5lTmFtZQpzdGFuZC5mdW4gPC0gZnVuY3Rpb24oeCl7KHgtbWVhbih4LG5hLnJtPVRSVUUpKS9zZCh4LG5hLnJtPVRSVUUpfQpzdGRTaWduYXR1cmU8LXN0YW5kLmZ1bihleHByZXNzaW9uRGF0YSRHUkFNRDFCKQpuYW1lcyhzdGRTaWduYXR1cmUpPC1yb3duYW1lcyhleHByZXNzaW9uRGF0YSkgI21ha2Ugc3VyZSB0aGV5IHJldGFpbiB0aGUgbmFtZSBvZiB0aGUgc2FtcGxlcwpgYGAKCmBgYHtyfQojTXVsdGlwbHkgYnkgYmV0YSBjb2VmZmljaWVudD8KI2JldGFDb2VmZiA8LSByZWFkLnRhYmxlKCJ+L0Rlc2t0b3AvTWVsYW5vbWEvYmV0YUNvZWZmLnRzdiIsIHNlcCA9ICJcdCIsIGhlYWRlciA9IFRSVUUsIHF1b3RlID0gIiIpCiNleHRyYWN0IGJldGEgY29lZmZpY2llbnQgZm9yIHRoZSBnZW5lIG9mIGludGVyZXN0CiNiZXRhQ29lZmZHZW5lIDwtIGJldGFDb2VmZltzZWxlY3QsICJFdmVudE1ldF9ZZXNfdnNfTm8iXQojd2VpZ2h0ZWRTaWduYXR1cmUgPC0gZXhwcmVzc2lvbkRhdGEqYmV0YUNvZWZmR2VuZQojc3RhbmRhcmRpemF0aW9uIG9mIHRoZSB3ZWlnaHRlZCBzaWduYXR1cmUKI3N0YW5kLmZ1biA8LSBmdW5jdGlvbih4KXsoeC1tZWFuKHgsbmEucm09VFJVRSkpL3NkKHgsbmEucm09VFJVRSl9CiNzdGRXZWlnaHRlZFNpZ25hdHVyZSA8LSBzdGFuZC5mdW4od2VpZ2h0ZWRTaWduYXR1cmUkR1JBTUQxQikKI25hbWVzKHN0ZFdlaWdodGVkU2lnbmF0dXJlKTwtcm93bmFtZXMod2VpZ2h0ZWRTaWduYXR1cmUpICNtYWtlIHN1cmUgdGhleSByZXRhaW4gdGhlIG5hbWUgb2YgdGhlIHNhbXBsZXMKYGBgCgpBcyBleHBlY3RlZCB0aGUgYWJzb2x1dGUgdmFsdWVzIGFmdGVyIHN0YW5kYXJkaXphdGlvbiBvZiB3ZWlnaHRlZCBzaWduYXR1cmUgKHN0YW5kLmZ1bih3ZWlnaHRlZFNpZ25hdHVyZVwkR1JBTUQxQikpIGlzIHNhbWUgYXMgdGhvc2Ugb2Ygc3RhbmRhcmRpemVkIGV4cHJlc3Npb24gdmFsdWVzIChzdGFuZC5mdW4oZXhwcmVzc2lvbkRhdGFcJEdSQU1EMUIpKS4gVGhlIG9ubHkgZGlmZmVyZW5jZSBjb21lcyBmcm9tIHNpZ24gb2YgdGhlIGJldGEgY29lZmZpY2llbnQgd2hpY2ggaXMgbmVnYXRpdmUgZm9yIEdSQU1EMUIKCmBgYHtyfQojTG9hZCBjbGluaWNhbCBkYXRhCmNsaW5pY2FsRGF0YTwtZGF0YS5mcmFtZShjb2xEYXRhKHZzZCkpCmNsaW5pY2FsRGF0YSRTaWduYXR1cmU8LSBzdGRTaWduYXR1cmVbcm93bmFtZXMoY2xpbmljYWxEYXRhKV0gI21lcmdlIHNpZ25hdHVyZSBpbiBjbGluaWNhbCBkYXRhIGZyYW1lCmBgYAoKI0NoZWNrIGFzc29jaWF0aW9uIG9mIEdSQU1EMUIgd2l0aCByZWxhcHNlCmBgYHtyfQpteV9jb21wYXJpc29ucyA8LSBsaXN0KCBjKCJZZXMiLCAiTm8iKSkKZ2dwbG90KGNsaW5pY2FsRGF0YVshaXMubmEoY2xpbmljYWxEYXRhJEV2ZW50TWV0KSxdLCBhZXMoeD1FdmVudE1ldCwgeT1TaWduYXR1cmUpKSsKICBnZW9tX3Zpb2xpbigpKwogIGdlb21faml0dGVyKGhlaWdodCA9IDAsIHdpZHRoID0gMC4xLCBhbHBoYSA9IDAuNSwgYWVzKCkpKwogICNzY2FsZV9maWxsX2JyZXdlcih0eXBlPSJxdWFsIiwgcGFsZXR0ZSA9ICJEYXJrMiIsIG5hbWUgPSAiQ2FtXzEyMSByaXNrIGdyb3VwIikrCiAgeGxhYigiRGlzdGFudCBtZXRhc3Rhc2VzIChBVkFTVC1NIFNraW4pIikrCiAgeWxhYigiVlNUIG5vcm1hbGl6ZWQgR1JBTUQxQiBleHByZXNzaW9uIChzdGFuZC4pIikrCiAgI2dlb21faGxpbmUoeWludGVyY2VwdCA9IHF1YW50aWxlKGNsaW5pY2FsRGF0YSRTaWduYXR1cmUsIDAuNzUpLCBjb2xvciA9ICJncmV5IiwgbGluZXR5cGUgPSAibG9uZ2Rhc2giKSsKICB0aGVtZSh0ZXh0PWVsZW1lbnRfdGV4dChzaXplPTcsICBmYW1pbHk9InNhbnMiKSkrCiAgZ2dwdWJyOjpzdGF0X2NvbXBhcmVfbWVhbnMoY29tcGFyaXNvbnMgPSBteV9jb21wYXJpc29ucywgbWV0aG9kID0gInQudGVzdCIsIHNpemUgPSAyLjUsIGZhbWlseT0ic2FucyIpIysgIyBBZGQgcGFpcndpc2UgY29tcGFyaXNvbnMgcC12YWx1ZQogICNnZ3B1YnI6OnN0YXRfY29tcGFyZV9tZWFucyhsYWJlbC55ID0gNiwgbWV0aG9kID0gImFub3ZhIiwgc2l6ZSA9IDIuNSwgZmFtaWx5PSJzYW5zIikjKwoKYGBgCkZpbmQgc3VpdGFibGUgY3V0LW9mZi4gQWZ0ZXIgdGFsa2luZyB0byBSb3ksIGl0IHNlZW1zIHRoYXQgd2UgY291bGQgZ28gZm9yIGEgd2VpZ2h0ZWQgR1JBTUQxQiB2YWx1ZSBvZiAwPwpMZXQncyBjaGVjayB0aGUgbWVkaWFuIHZhbHVlLiBNYXkgYmUgSSBjYW4gdGFrZSB0aGUgbWVkaWFuIGN1dC1vZmYgYW5kIHBlcmZvcm0gY2hpLXNxdWFyZWQgdGVzdCB0byBjaGVjayBpZiB0aGVyZSBpcyBubyBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIG1lYW4gb2YgMiBncm91cHM/CmBgYHtyfQojRGl2aWRlIGluIGhpZ2gvbG93IGdyb3VwcyBiYXNlZCBvbiBtZWFuIGFuZCBtZWRpYW4KY29fbWVhbjwtbWVhbihjbGluaWNhbERhdGEkU2lnbmF0dXJlKQpjb19tZWRpYW48LW1lZGlhbihjbGluaWNhbERhdGEkU2lnbmF0dXJlKQpjb18wXzMzPC1xdWFudGlsZShjbGluaWNhbERhdGEkU2lnbmF0dXJlLCAwLjMzKQpjb18wXzY2PC1xdWFudGlsZShjbGluaWNhbERhdGEkU2lnbmF0dXJlLCAwLjY2KQppZiAodGlzc3VlPT0iU2tpbiIpIHsKICBjb19kZW5zaXR5X2ludGVyc2VjdGlvbjwtLTAuMjIzMTU2MTc5IzAuMjM1MjAyOTM5Cn1lbHNlewogIGNvX2RlbnNpdHlfaW50ZXJzZWN0aW9uPC0wLjM0ODEwNTcxMTUjLTAuMjIzMTU2MTc5IzAuMjM1MjAyOTM5Cn0Kc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKGxpYnJhcnkocGxvdGx5KSkKcDwtZ2dwbG90KGRhdGEgPSBjbGluaWNhbERhdGEsIGFlcyh4ID0gU2lnbmF0dXJlLCBmaWxsPUV2ZW50TWV0KSkgKwogIGdlb21fZGVuc2l0eShhbHBoYT0wLjMpKwogIGdlb21fdmxpbmUoeGludGVyY2VwdD1jKGNvX21lYW4sIGNvX21lZGlhbiwgY29fMF8zMywgY29fMF82NiwgY29fZGVuc2l0eV9pbnRlcnNlY3Rpb24pLCBsaW5ldHlwZT1jKCd0d29kYXNoJywgJ2xvbmdkYXNoJywgJ2RvdHRlZCcsICdkb3RkYXNoJywgJ2Rhc2hlZCcpKSsKICBnZ3RpdGxlKHRpc3N1ZSkKZ2dwbG90bHkocCkgCmBgYAoKYGBge3J9CmNsaW5pY2FsRGF0YSRTaWduYXR1cmVHcm91cE1lYW4gPC0gYXMuZmFjdG9yKGlmZWxzZShjbGluaWNhbERhdGEkU2lnbmF0dXJlID49IGFzLm51bWVyaWMoY29fbWVhbiksICJIaWdoIiwgIkxvdyIpKQpjbGluaWNhbERhdGEkU2lnbmF0dXJlR3JvdXBNZWRpYW4gPC0gYXMuZmFjdG9yKGlmZWxzZShjbGluaWNhbERhdGEkU2lnbmF0dXJlID49IGFzLm51bWVyaWMoY29fbWVkaWFuKSwgIkhpZ2giLCAiTG93IikpCmNsaW5pY2FsRGF0YSRTaWduYXR1cmVHcm91cDBfMzMgPC0gYXMuZmFjdG9yKGlmZWxzZShjbGluaWNhbERhdGEkU2lnbmF0dXJlID49IGFzLm51bWVyaWMoY29fMF8zMyksICJIaWdoIiwgIkxvdyIpKQpjbGluaWNhbERhdGEkU2lnbmF0dXJlR3JvdXAwXzY2IDwtIGFzLmZhY3RvcihpZmVsc2UoY2xpbmljYWxEYXRhJFNpZ25hdHVyZSA+PSBhcy5udW1lcmljKGNvXzBfNjYpLCAiSGlnaCIsICJMb3ciKSkKY2xpbmljYWxEYXRhJFNpZ25hdHVyZUdyb3VwRGVuc2l0eUludGVyc2VjdGlvbiA8LSBhcy5mYWN0b3IoaWZlbHNlKGNsaW5pY2FsRGF0YSRTaWduYXR1cmUgPj0gYXMubnVtZXJpYyhjb19kZW5zaXR5X2ludGVyc2VjdGlvbiksICJIaWdoIiwgIkxvdyIpKQoKcHJpbnQoY2hpc3EudGVzdChjbGluaWNhbERhdGEkU2lnbmF0dXJlR3JvdXBNZWFuLCBjbGluaWNhbERhdGEkRXZlbnRNZXQpKQpwcmludChjaGlzcS50ZXN0KGNsaW5pY2FsRGF0YSRTaWduYXR1cmVHcm91cE1lZGlhbiwgY2xpbmljYWxEYXRhJEV2ZW50TWV0KSkKcHJpbnQoY2hpc3EudGVzdChjbGluaWNhbERhdGEkU2lnbmF0dXJlR3JvdXAwXzMzLCBjbGluaWNhbERhdGEkRXZlbnRNZXQpKQpwcmludChjaGlzcS50ZXN0KGNsaW5pY2FsRGF0YSRTaWduYXR1cmVHcm91cDBfNjYsIGNsaW5pY2FsRGF0YSRFdmVudE1ldCkpCnByaW50KGNoaXNxLnRlc3QoY2xpbmljYWxEYXRhJFNpZ25hdHVyZUdyb3VwRGVuc2l0eUludGVyc2VjdGlvbiwgY2xpbmljYWxEYXRhJEV2ZW50TWV0KSkKYGBgCiNDaGVjayBhc3NvY2lhdGlvbiBvZiBOUkFTIHdpdGggb3RoZXIgY2xpbmljYWwgY292YXJpYXRlcwpgYGB7cn0KcHJpbnQoY2hpc3EudGVzdChjbGluaWNhbERhdGEkTlJBUywgY2xpbmljYWxEYXRhJEV2ZW50TWV0KSkKcHJpbnQoY2hpc3EudGVzdChjbGluaWNhbERhdGEkTlJBUywgY2xpbmljYWxEYXRhJEJyZXMpKQpwcmludChjaGlzcS50ZXN0KGNsaW5pY2FsRGF0YSROUkFTLCBjbGluaWNhbERhdGEkVWxjKSkKYGBgCiNDaGVjayBhc3NvY2lhdGlvbiBvZiBHUkFNRDFCIHdpdGggdWxjZXJhdGlvbgpgYGB7cn0KbXlfY29tcGFyaXNvbnMgPC0gbGlzdCggYygiUHJlc2VudCIsICJBYnNlbnQiKSkKZ2dwbG90KGNsaW5pY2FsRGF0YVshaXMubmEoY2xpbmljYWxEYXRhJFVsYyksXSwgYWVzKHg9VWxjLCB5PVNpZ25hdHVyZSkpKwogIGdlb21fdmlvbGluKCkrCiAgZ2VvbV9qaXR0ZXIoaGVpZ2h0ID0gMCwgd2lkdGggPSAwLjEsIGFscGhhID0gMC41LCBhZXMoKSkrCiAgI3NjYWxlX2ZpbGxfYnJld2VyKHR5cGU9InF1YWwiLCBwYWxldHRlID0gIkRhcmsyIiwgbmFtZSA9ICJDYW1fMTIxIHJpc2sgZ3JvdXAiKSsKICB4bGFiKCJVbGMgKEFWQVNULU0gU2tpbikiKSsKICB5bGFiKCJWU1Qgbm9ybWFsaXplZCBHUkFNRDFCIGV4cHJlc3Npb24gKHN0YW5kLikiKSsKICAjZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gcXVhbnRpbGUoY2xpbmljYWxEYXRhJFNpZ25hdHVyZSwgMC43NSksIGNvbG9yID0gImdyZXkiLCBsaW5ldHlwZSA9ICJsb25nZGFzaCIpKwogIHRoZW1lKHRleHQ9ZWxlbWVudF90ZXh0KHNpemU9NywgIGZhbWlseT0ic2FucyIpKSsKICBnZ3B1YnI6OnN0YXRfY29tcGFyZV9tZWFucyhjb21wYXJpc29ucyA9IG15X2NvbXBhcmlzb25zLCBtZXRob2QgPSAidC50ZXN0Iiwgc2l6ZSA9IDIuNSwgZmFtaWx5PSJzYW5zIikjKyAjIEFkZCBwYWlyd2lzZSBjb21wYXJpc29ucyBwLXZhbHVlCiAgI2dncHVicjo6c3RhdF9jb21wYXJlX21lYW5zKGxhYmVsLnkgPSA2LCBtZXRob2QgPSAiYW5vdmEiLCBzaXplID0gMi41LCBmYW1pbHk9InNhbnMiKSMrCmBgYAoKI0NoZWNrIGFzc29jaWF0aW9uIG9mIEdSQU1EMUIgd2l0aCBCcmVzCmBgYHtyfQpteV9jb21wYXJpc29ucyA8LSBsaXN0KCBjKCI8PSAyLjAgbW0iLCAiPjItNG1tIiksIGMoIjw9IDIuMCBtbSIsICI+NC4wbW0iKSwgYygiPjItNG1tIiwgIj40LjBtbSIpKQpnZ3Bsb3QoY2xpbmljYWxEYXRhWyFpcy5uYShjbGluaWNhbERhdGEkQnJlcyksXSwgYWVzKHg9QnJlcywgeT1TaWduYXR1cmUpKSsKICBnZW9tX3Zpb2xpbigpKwogIGdlb21faml0dGVyKGhlaWdodCA9IDAsIHdpZHRoID0gMC4xLCBhbHBoYSA9IDAuNSwgYWVzKCkpKwogICNzY2FsZV9maWxsX2JyZXdlcih0eXBlPSJxdWFsIiwgcGFsZXR0ZSA9ICJEYXJrMiIsIG5hbWUgPSAiQ2FtXzEyMSByaXNrIGdyb3VwIikrCiAgeGxhYigiQnJlcyAoQVZBU1QtTSBTa2luKSIpKwogIHlsYWIoIlZTVCBub3JtYWxpemVkIEdSQU1EMUIgZXhwcmVzc2lvbiAoc3RhbmQuKSIpKwogICNnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBxdWFudGlsZShjbGluaWNhbERhdGEkU2lnbmF0dXJlLCAwLjc1KSwgY29sb3IgPSAiZ3JleSIsIGxpbmV0eXBlID0gImxvbmdkYXNoIikrCiAgdGhlbWUodGV4dD1lbGVtZW50X3RleHQoc2l6ZT03LCAgZmFtaWx5PSJzYW5zIikpKwogIGdncHVicjo6c3RhdF9jb21wYXJlX21lYW5zKGNvbXBhcmlzb25zID0gbXlfY29tcGFyaXNvbnMsIG1ldGhvZCA9ICJ0LnRlc3QiLCBzaXplID0gMi41LCBmYW1pbHk9InNhbnMiKSsgIyBBZGQgcGFpcndpc2UgY29tcGFyaXNvbnMgcC12YWx1ZQogIGdncHVicjo6c3RhdF9jb21wYXJlX21lYW5zKGxhYmVsLnkgPSA2LCBtZXRob2QgPSAiYW5vdmEiLCBzaXplID0gMi41LCBmYW1pbHk9InNhbnMiKSMrCmBgYAoKI0NoZWNrIGFzc29jaWF0aW9uIG9mIEdSQU1EMUIgd2l0aCBTdGFnZQpgYGB7cn0KbXlfY29tcGFyaXNvbnMgPC0gbGlzdCggYygiSUlCIiwgIklJQyIpLCBjKCJJSUIiLCAiSUlJQSIpLCBjKCJJSUIiLCAiSUlJQiIpLCBjKCJJSUIiLCAiSUlJQyIpLAogICAgICAgICAgICAgICAgICAgICAgICBjKCJJSUMiLCAiSUlJQSIpLCBjKCJJSUMiLCAiSUlJQiIpLCBjKCJJSUMiLCAiSUlJQyIpLAogICAgICAgICAgICAgICAgICAgICAgICBjKCJJSUlBIiwgIklJSUIiKSwgYygiSUlJQSIsICJJSUlDIiksCiAgICAgICAgICAgICAgICAgICAgICAgIGMoIklJSUIiLCAiSUlJQyIpKQpnZ3Bsb3QoY2xpbmljYWxEYXRhWyFpcy5uYShjbGluaWNhbERhdGEkU3RhZ2UpLF0sIGFlcyh4PVN0YWdlLCB5PVNpZ25hdHVyZSkpKwogIGdlb21fdmlvbGluKCkrCiAgZ2VvbV9qaXR0ZXIoaGVpZ2h0ID0gMCwgd2lkdGggPSAwLjEsIGFscGhhID0gMC41LCBhZXMoKSkrCiAgI3NjYWxlX2ZpbGxfYnJld2VyKHR5cGU9InF1YWwiLCBwYWxldHRlID0gIkRhcmsyIiwgbmFtZSA9ICJDYW1fMTIxIHJpc2sgZ3JvdXAiKSsKICB4bGFiKCJTdGFnZSAoQVZBU1QtTSBTa2luKSIpKwogIHlsYWIoIlZTVCBub3JtYWxpemVkIEdSQU1EMUIgZXhwcmVzc2lvbiAoc3RhbmQuKSIpKwogICNnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBxdWFudGlsZShjbGluaWNhbERhdGEkU2lnbmF0dXJlLCAwLjc1KSwgY29sb3IgPSAiZ3JleSIsIGxpbmV0eXBlID0gImxvbmdkYXNoIikrCiAgdGhlbWUodGV4dD1lbGVtZW50X3RleHQoc2l6ZT03LCAgZmFtaWx5PSJzYW5zIikpKwogIGdncHVicjo6c3RhdF9jb21wYXJlX21lYW5zKGNvbXBhcmlzb25zID0gbXlfY29tcGFyaXNvbnMsIG1ldGhvZCA9ICJ0LnRlc3QiLCBzaXplID0gMi41LCBmYW1pbHk9InNhbnMiKSsgIyBBZGQgcGFpcndpc2UgY29tcGFyaXNvbnMgcC12YWx1ZQogIGdncHVicjo6c3RhdF9jb21wYXJlX21lYW5zKGxhYmVsLnkgPSA5LCBtZXRob2QgPSAiYW5vdmEiLCBzaXplID0gMi41LCBmYW1pbHk9InNhbnMiKSMrCmBgYApgYGB7cn0KZ2dwbG90KGNsaW5pY2FsRGF0YVshaXMubmEoY2xpbmljYWxEYXRhJFN0YWdlX2JpbmFyeSksXSwgYWVzKHg9U3RhZ2VfYmluYXJ5LCB5PVNpZ25hdHVyZSkpKwogIGdlb21fdmlvbGluKCkrCiAgZ2VvbV9qaXR0ZXIoaGVpZ2h0ID0gMCwgd2lkdGggPSAwLjEsIGFscGhhID0gMC41LCBhZXMoKSkrCiAgI3NjYWxlX2ZpbGxfYnJld2VyKHR5cGU9InF1YWwiLCBwYWxldHRlID0gIkRhcmsyIiwgbmFtZSA9ICJDYW1fMTIxIHJpc2sgZ3JvdXAiKSsKICB4bGFiKCJTdGFnZSAoQVZBU1QtTSBTa2luKSIpKwogIHlsYWIoIlZTVCBub3JtYWxpemVkIEdSQU1EMUIgZXhwcmVzc2lvbiAoc3RhbmQuKSIpKwogICNnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBxdWFudGlsZShjbGluaWNhbERhdGEkU2lnbmF0dXJlLCAwLjc1KSwgY29sb3IgPSAiZ3JleSIsIGxpbmV0eXBlID0gImxvbmdkYXNoIikrCiAgdGhlbWUodGV4dD1lbGVtZW50X3RleHQoc2l6ZT03LCAgZmFtaWx5PSJzYW5zIikpKwogIGdncHVicjo6c3RhdF9jb21wYXJlX21lYW5zKG1ldGhvZCA9ICJ0LnRlc3QiLCBzaXplID0gMi41LCBmYW1pbHk9InNhbnMiKSMrICMgQWRkIHBhaXJ3aXNlIGNvbXBhcmlzb25zIHAtdmFsdWUKICAjZ2dwdWJyOjpzdGF0X2NvbXBhcmVfbWVhbnMobGFiZWwueSA9IDksIG1ldGhvZCA9ICJhbm92YSIsIHNpemUgPSAyLjUsIGZhbWlseT0ic2FucyIpCmBgYAoKI0NoZWNrIGFzc29jaWF0aW9uIG9mIEdSQU1EMUIgd2l0aCBTaXRlCmBgYHtyfQpteV9jb21wYXJpc29ucyA8LSBsaXN0KCBjKCJIZWFkIGFuZCBuZWNrIiwgIkxvd2VyIGxpbXMiKSwgYygiSGVhZCBhbmQgbmVjayIsICJUcnVuayIpLCBjKCJIZWFkIGFuZCBuZWNrIiwgIlVwcGVyIGxpbWJzIiksCiAgICAgICAgICAgICAgICAgICAgICAgIGMoIkxvd2VyIGxpbXMiLCAiVHJ1bmsiKSwgYygiTG93ZXIgbGltcyIsICJVcHBlciBsaW1icyIpLAogICAgICAgICAgICAgICAgICAgICAgICBjKCJUcnVuayIsICJVcHBlciBsaW1icyIpKQpnZ3Bsb3QoY2xpbmljYWxEYXRhWyFpcy5uYShjbGluaWNhbERhdGEkU2l0ZSksXSwgYWVzKHg9U2l0ZSwgeT1TaWduYXR1cmUpKSsKICBnZW9tX3Zpb2xpbigpKwogIGdlb21faml0dGVyKGhlaWdodCA9IDAsIHdpZHRoID0gMC4xLCBhbHBoYSA9IDAuNSwgYWVzKCkpKwogICNzY2FsZV9maWxsX2JyZXdlcih0eXBlPSJxdWFsIiwgcGFsZXR0ZSA9ICJEYXJrMiIsIG5hbWUgPSAiQ2FtXzEyMSByaXNrIGdyb3VwIikrCiAgeGxhYigiU2l0ZSAoQVZBU1QtTSBTa2luKSIpKwogIHlsYWIoIlZTVCBub3JtYWxpemVkIEdSQU1EMUIgZXhwcmVzc2lvbiAoc3RhbmQuKSIpKwogICNnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBxdWFudGlsZShjbGluaWNhbERhdGEkU2lnbmF0dXJlLCAwLjc1KSwgY29sb3IgPSAiZ3JleSIsIGxpbmV0eXBlID0gImxvbmdkYXNoIikrCiAgdGhlbWUodGV4dD1lbGVtZW50X3RleHQoc2l6ZT03LCAgZmFtaWx5PSJzYW5zIikpKwogIGdncHVicjo6c3RhdF9jb21wYXJlX21lYW5zKGNvbXBhcmlzb25zID0gbXlfY29tcGFyaXNvbnMsIG1ldGhvZCA9ICJ0LnRlc3QiLCBzaXplID0gMi41LCBmYW1pbHk9InNhbnMiKSsgIyBBZGQgcGFpcndpc2UgY29tcGFyaXNvbnMgcC12YWx1ZQogIGdncHVicjo6c3RhdF9jb21wYXJlX21lYW5zKGxhYmVsLnkgPSA5LCBtZXRob2QgPSAiYW5vdmEiLCBzaXplID0gMi41LCBmYW1pbHk9InNhbnMiKSMrCgpgYGAKCiNDaGVjayBhc3NvY2lhdGlvbiBvZiBHUkFNRDFCIHdpdGggQlJBRiBtdXRhdGlvbgpgYGB7cn0KbXlfY29tcGFyaXNvbnMgPC0gbGlzdCggYygiVjYwMEUiLCAiV1QiKSkKZ2dwbG90KGNsaW5pY2FsRGF0YVshaXMubmEoY2xpbmljYWxEYXRhJEJSQUYpLF0sIGFlcyh4PUJSQUYsIHk9U2lnbmF0dXJlKSkrCiAgZ2VvbV92aW9saW4oKSsKICBnZW9tX2ppdHRlcihoZWlnaHQgPSAwLCB3aWR0aCA9IDAuMSwgYWxwaGEgPSAwLjUsIGFlcygpKSsKICAjc2NhbGVfZmlsbF9icmV3ZXIodHlwZT0icXVhbCIsIHBhbGV0dGUgPSAiRGFyazIiLCBuYW1lID0gIkNhbV8xMjEgcmlzayBncm91cCIpKwogIHhsYWIoIkJSQUYgKEFWQVNULU0gU2tpbikiKSsKICB5bGFiKCJWU1Qgbm9ybWFsaXplZCBHUkFNRDFCIGV4cHJlc3Npb24gKHN0YW5kLikiKSsKICAjZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gcXVhbnRpbGUoY2xpbmljYWxEYXRhJFNpZ25hdHVyZSwgMC43NSksIGNvbG9yID0gImdyZXkiLCBsaW5ldHlwZSA9ICJsb25nZGFzaCIpKwogIHRoZW1lKHRleHQ9ZWxlbWVudF90ZXh0KHNpemU9NywgIGZhbWlseT0ic2FucyIpKSsKICBnZ3B1YnI6OnN0YXRfY29tcGFyZV9tZWFucyhjb21wYXJpc29ucyA9IG15X2NvbXBhcmlzb25zLCBtZXRob2QgPSAidC50ZXN0Iiwgc2l6ZSA9IDIuNSwgZmFtaWx5PSJzYW5zIikjKyAjIEFkZCBwYWlyd2lzZSBjb21wYXJpc29ucyBwLXZhbHVlCiAgI2dncHVicjo6c3RhdF9jb21wYXJlX21lYW5zKGxhYmVsLnkgPSA5LCBtZXRob2QgPSAiYW5vdmEiLCBzaXplID0gMi41LCBmYW1pbHk9InNhbnMiKSMrCmBgYAoKI0NoZWNrIGFzc29jaWF0aW9uIG9mIEdSQU1EMUIgd2l0aCBOUkFTIG11dGF0aW9uCmBgYHtyfQpteV9jb21wYXJpc29ucyA8LSBsaXN0KCBjKCJNdXRhbnQiLCAiV1QiKSkKZ2dwbG90KGNsaW5pY2FsRGF0YVshaXMubmEoY2xpbmljYWxEYXRhJE5SQVMpLF0sIGFlcyh4PU5SQVMsIHk9U2lnbmF0dXJlKSkrCiAgZ2VvbV92aW9saW4oKSsKICBnZW9tX2ppdHRlcihoZWlnaHQgPSAwLCB3aWR0aCA9IDAuMSwgYWxwaGEgPSAwLjUsIGFlcygpKSsKICAjc2NhbGVfZmlsbF9icmV3ZXIodHlwZT0icXVhbCIsIHBhbGV0dGUgPSAiRGFyazIiLCBuYW1lID0gIkNhbV8xMjEgcmlzayBncm91cCIpKwogIHhsYWIoIk5SQVMgKEFWQVNULU0gU2tpbikiKSsKICB5bGFiKCJWU1Qgbm9ybWFsaXplZCBHUkFNRDFCIGV4cHJlc3Npb24gKHN0YW5kLikiKSsKICAjZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gcXVhbnRpbGUoY2xpbmljYWxEYXRhJFNpZ25hdHVyZSwgMC43NSksIGNvbG9yID0gImdyZXkiLCBsaW5ldHlwZSA9ICJsb25nZGFzaCIpKwogIHRoZW1lKHRleHQ9ZWxlbWVudF90ZXh0KHNpemU9NywgIGZhbWlseT0ic2FucyIpKSsKICBnZ3B1YnI6OnN0YXRfY29tcGFyZV9tZWFucyhjb21wYXJpc29ucyA9IG15X2NvbXBhcmlzb25zLCBtZXRob2QgPSAidC50ZXN0Iiwgc2l6ZSA9IDIuNSwgZmFtaWx5PSJzYW5zIikjKyAjIEFkZCBwYWlyd2lzZSBjb21wYXJpc29ucyBwLXZhbHVlCiAgI2dncHVicjo6c3RhdF9jb21wYXJlX21lYW5zKGxhYmVsLnkgPSA5LCBtZXRob2QgPSAiYW5vdmEiLCBzaXplID0gMi41LCBmYW1pbHk9InNhbnMiKSMrCmBgYAoKI0NoZWNrIGFzc29jaWF0aW9uIG9mIEdSQU1EMUIgd2l0aCBUSUwgY291bnRzCmBgYHtyfQpqdF9uc19zbV9zY29yZXMgPSByZWFkLnRhYmxlKCIuLi8uLi8uLi9KVF9OU19TTV9Db21iaW5lZC54bHMgLSBKVF9OU19TTS50c3YiLCBzZXAgPSAiXHQiLCBoZWFkZXIgPSBUUlVFLCBxdW90ZSA9ICIiKQpjb21iaW5lZFNjb3JlIDwtIGRhdGEuZnJhbWUoIlNjYW5uZWRfZmlsZS5tZSI9IGp0X25zX3NtX3Njb3JlcyRKVF9TY2FubmVkX2ZpbGUubWUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJSLlNlcV9zYW1wbGVJRCI9IGp0X25zX3NtX3Njb3JlcyRKVF9SLlNlcV9zYW1wbGVJRCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNsYXJrU2NvcmUiPSBpZmVsc2UoanRfbnNfc21fc2NvcmVzJEpUX0NsYXJrX3Njb3JlPT1qdF9uc19zbV9zY29yZXMkTlNfQ2xhcmtfc2NvcmUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBqdF9uc19zbV9zY29yZXMkSlRfQ2xhcmtfc2NvcmUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAganRfbnNfc21fc2NvcmVzJFNNX0NsYXJrX3Njb3JlKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIk1JQVNjb3JlIj0gaWZlbHNlKGp0X25zX3NtX3Njb3JlcyRKVF9USUxfZ3JhZGU9PWp0X25zX3NtX3Njb3JlcyROU19USUwuR1JBREUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBqdF9uc19zbV9zY29yZXMkSlRfVElMX2dyYWRlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGp0X25zX3NtX3Njb3JlcyRTTV9USUxfZ3JhZGUpKQojUmVtb3ZlIGR1cGxpY2F0ZWQgZW50cmllcwpjb21iaW5lZFNjb3JlPC1jb21iaW5lZFNjb3JlWyFkdXBsaWNhdGVkKGNvbWJpbmVkU2NvcmUkUi5TZXFfc2FtcGxlSUQpLCBdCnJvd25hbWVzKGNvbWJpbmVkU2NvcmUpPC1jb21iaW5lZFNjb3JlJFIuU2VxX3NhbXBsZUlEICNyZW5hbWUgcm93bmFtZXMgd2l0aCBzYW1wbGUgaWRzCmBgYAoKYGBge3J9CnJvd25hbWVzKGNsaW5pY2FsRGF0YSk8LWNsaW5pY2FsRGF0YSRSTkEuU2VxLlNhbXBsZQpkZjwtZGF0YS5mcmFtZSgiRXZlbnRNZXQiPWNsaW5pY2FsRGF0YSRFdmVudE1ldCwKICAgICAgICAgICAgICAgIkNsYXJrU2NvcmUiPWNvbWJpbmVkU2NvcmVbcm93bmFtZXMoY2xpbmljYWxEYXRhKSwgIkNsYXJrU2NvcmUiXSwKICAgICAgICAgICAgICAgIk1JQVNjb3JlIj1jb21iaW5lZFNjb3JlW3Jvd25hbWVzKGNsaW5pY2FsRGF0YSksICJNSUFTY29yZSJdLAogICAgICAgICAgICAgICAiU2lnbmF0dXJlIj1jbGluaWNhbERhdGEkU2lnbmF0dXJlKQpyb3duYW1lcyhkZik8LXJvd25hbWVzKGNsaW5pY2FsRGF0YSkKCmRmJENsYXJrU2NvcmUgPC0gYXMuZmFjdG9yKGRmJENsYXJrU2NvcmUpCmxldmVscyhkZiRDbGFya1Njb3JlKSA8LSBjKCJhYnNlbnQiLCAibm9uYnJpc2siLCAiYnJpc2siKQpgYGAKCmBgYHtyfQpteV9jb21wYXJpc29ucyA8LSBsaXN0KCBjKCJhYnNlbnQiLCAibm9uYnJpc2siKSwgYygibm9uYnJpc2siLCAiYnJpc2siKSwgYygiYWJzZW50IiwgImJyaXNrIikgKQojZGYkSlRfQ2xhcmtfc2NvcmUgPC0gZmFjdG9yKGRmJEpUX0NsYXJrX3Njb3JlLCBsZXZlbHMgPSBjKCJhYnNlbnQiLCAibm9uYnJpc2siLCAiYnJpc2siKSkKZ2dwbG90KGRmWyFpcy5uYShkZiRDbGFya1Njb3JlKSwgXSwgYWVzKHg9Q2xhcmtTY29yZSwgeT1TaWduYXR1cmUjLCBjb2xvcj1EUkJyYWluCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApKSsKICBnZW9tX3Zpb2xpbigpKwogIGdlb21faml0dGVyKGhlaWdodCA9IDAsIHdpZHRoID0gMC4xLCBhbHBoYSA9IDAuNSwgYWVzKCkpKwogICNzY2FsZV9maWxsX2JyZXdlcih0eXBlPSJxdWFsIiwgcGFsZXR0ZSA9ICJEYXJrMiIsIG5hbWUgPSAiQ2FtXzEyMSByaXNrIGdyb3VwIikrCiAgeGxhYigiQ2xhcmsgc2NvcmUgKEFWQVNULU0gU2tpbikiKSsKICB5bGFiKCJWU1Qgbm9ybWFsaXplZCBHUkFNRDFCIGV4cHJlc3Npb24gKHN0YW5kLikiKSsKICAjZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gcXVhbnRpbGUoZGYkU2lnbmF0dXJlLCAwLjc1KSwgY29sb3IgPSAiZ3JleSIsIGxpbmV0eXBlID0gImxvbmdkYXNoIikrCiAgdGhlbWUodGV4dD1lbGVtZW50X3RleHQoc2l6ZT03LCAgZmFtaWx5PSJzYW5zIikpKwogIGdncHVicjo6c3RhdF9jb21wYXJlX21lYW5zKGNvbXBhcmlzb25zID0gbXlfY29tcGFyaXNvbnMsIG1ldGhvZCA9ICJ0LnRlc3QiLCBzaXplID0gMi41LCBmYW1pbHk9InNhbnMiKSsgIyBBZGQgcGFpcndpc2UgY29tcGFyaXNvbnMgcC12YWx1ZQogIGdncHVicjo6c3RhdF9jb21wYXJlX21lYW5zKGxhYmVsLnkgPSA2LCBtZXRob2QgPSAiYW5vdmEiLCBzaXplID0gMi41LCBmYW1pbHk9InNhbnMiKSMrCiAjIGZhY2V0X3dyYXAofkRSQnJhaW4pIyBBZGQgZ2xvYmFsIHAtdmFsdWUKYGBgCgpgYGB7cn0KbXlfY29tcGFyaXNvbnMgPC0gbGlzdCggYygiMCIsICIxIiksIGMoIjAiLCAiMiIpLCBjKCIwIiwgIjMiKSwgYygiMSIsICIyIiksIGMoIjEiLCAiMyIpLCBjKCIyIiwgIjMiKSkKZGYkTUlBU2NvcmUgPC0gYXMuZmFjdG9yKGRmJE1JQVNjb3JlKQpnZ3Bsb3QoZGZbIWlzLm5hKGRmJE1JQVNjb3JlKSwgXSwgYWVzKHg9TUlBU2NvcmUsIHk9U2lnbmF0dXJlIywgY29sb3I9RFJCcmFpbgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkpKwogIGdlb21fdmlvbGluKCkrCiAgZ2VvbV9qaXR0ZXIoaGVpZ2h0ID0gMCwgd2lkdGggPSAwLjEsIGFscGhhID0gMC41KSsKICBzY2FsZV9maWxsX2JyZXdlcih0eXBlPSJxdWFsIiwgcGFsZXR0ZSA9ICJEYXJrMiIsIG5hbWUgPSAiTUlBIHNjb3JlIikrCiAgeGxhYigiTUlBIHNjb3JlIChBVkFTVC1NIFNraW4pIikrCiAgeWxhYigiVlNUIG5vcm1hbGl6ZWQgR1JBTUQxQiBleHByZXNzaW9uIChzdGFuZC4pIikrCiAgI2dlb21faGxpbmUoeWludGVyY2VwdCA9IHF1YW50aWxlKGRmJFNpZ25hdHVyZSwgMC43NSksIGNvbG9yID0gImdyZXkiLCBsaW5ldHlwZSA9ICJsb25nZGFzaCIpKwogIHRoZW1lKHRleHQ9ZWxlbWVudF90ZXh0KHNpemU9NywgIGZhbWlseT0ic2FucyIpKSsKICBnZ3B1YnI6OnN0YXRfY29tcGFyZV9tZWFucyhjb21wYXJpc29ucyA9IG15X2NvbXBhcmlzb25zLCBtZXRob2QgPSAidC50ZXN0Iiwgc2l6ZSA9IDIuNSwgZmFtaWx5PSJzYW5zIikrICMgQWRkIHBhaXJ3aXNlIGNvbXBhcmlzb25zIHAtdmFsdWUKICBnZ3B1YnI6OnN0YXRfY29tcGFyZV9tZWFucyhsYWJlbC55ID0gNiwgbWV0aG9kID0gImFub3ZhIiwgc2l6ZSA9IDIuNSwgZmFtaWx5PSJzYW5zIikjKwogICNmYWNldF93cmFwKH5EUkJyYWluKQogICAgICMgQWRkIGdsb2JhbCBwLXZhbHVlCgpgYGAKYGBge3J9CiNteV9jb21wYXJpc29ucyA8LSBsaXN0KCBjKCJNdXRhbnQiLCAiV1QiKSkKZ2dwbG90KGNsaW5pY2FsRGF0YVshaXMubmEoY2xpbmljYWxEYXRhJHRyZWF0bWVudCksXSwgYWVzKHg9dHJlYXRtZW50LCB5PVNpZ25hdHVyZSkpKwogIGdlb21fdmlvbGluKCkrCiAgZ2VvbV9qaXR0ZXIoaGVpZ2h0ID0gMCwgd2lkdGggPSAwLjEsIGFscGhhID0gMC41LCBhZXMoKSkrCiAgI3NjYWxlX2ZpbGxfYnJld2VyKHR5cGU9InF1YWwiLCBwYWxldHRlID0gIkRhcmsyIiwgbmFtZSA9ICJDYW1fMTIxIHJpc2sgZ3JvdXAiKSsKICB4bGFiKCJUcmVhdG1lbnQgKEFWQVNULU0gU2tpbikiKSsKICB5bGFiKCJWU1Qgbm9ybWFsaXplZCBHUkFNRDFCIGV4cHJlc3Npb24gKHN0YW5kLikiKSsKICAjZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gcXVhbnRpbGUoY2xpbmljYWxEYXRhJFNpZ25hdHVyZSwgMC43NSksIGNvbG9yID0gImdyZXkiLCBsaW5ldHlwZSA9ICJsb25nZGFzaCIpKwogIHRoZW1lKHRleHQ9ZWxlbWVudF90ZXh0KHNpemU9NywgIGZhbWlseT0ic2FucyIpKSsKICBnZ3B1YnI6OnN0YXRfY29tcGFyZV9tZWFucyhtZXRob2QgPSAidC50ZXN0Iiwgc2l6ZSA9IDIuNSwgZmFtaWx5PSJzYW5zIikjKyAjIEFkZCBwYWlyd2lzZSBjb21wYXJpc29ucyBwLXZhbHVlCiAgI2dncHVicjo6c3RhdF9jb21wYXJlX21lYW5zKGxhYmVsLnkgPSA5LCBtZXRob2QgPSAiYW5vdmEiLCBzaXplID0gMi41LCBmYW1pbHk9InNhbnMiKSMrCmBgYApgYGB7cn0KI215X2NvbXBhcmlzb25zIDwtIGxpc3QoIGMoIk11dGFudCIsICJXVCIpKQpnZ3Bsb3QoY2xpbmljYWxEYXRhWyFpcy5uYShjbGluaWNhbERhdGEkRUNPRyksXSwgYWVzKHg9RUNPRywgeT1TaWduYXR1cmUpKSsKICBnZW9tX3Zpb2xpbigpKwogIGdlb21faml0dGVyKGhlaWdodCA9IDAsIHdpZHRoID0gMC4xLCBhbHBoYSA9IDAuNSwgYWVzKCkpKwogICNzY2FsZV9maWxsX2JyZXdlcih0eXBlPSJxdWFsIiwgcGFsZXR0ZSA9ICJEYXJrMiIsIG5hbWUgPSAiQ2FtXzEyMSByaXNrIGdyb3VwIikrCiAgeGxhYigiRUNPRyAoQVZBU1QtTSBTa2luKSIpKwogIHlsYWIoIlZTVCBub3JtYWxpemVkIEdSQU1EMUIgZXhwcmVzc2lvbiAoc3RhbmQuKSIpKwogICNnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBxdWFudGlsZShjbGluaWNhbERhdGEkU2lnbmF0dXJlLCAwLjc1KSwgY29sb3IgPSAiZ3JleSIsIGxpbmV0eXBlID0gImxvbmdkYXNoIikrCiAgdGhlbWUodGV4dD1lbGVtZW50X3RleHQoc2l6ZT03LCAgZmFtaWx5PSJzYW5zIikpKwogIGdncHVicjo6c3RhdF9jb21wYXJlX21lYW5zKG1ldGhvZCA9ICJ0LnRlc3QiLCBzaXplID0gMi41LCBmYW1pbHk9InNhbnMiKSMrICMgQWRkIHBhaXJ3aXNlIGNvbXBhcmlzb25zIHAtdmFsdWUKICAjZ2dwdWJyOjpzdGF0X2NvbXBhcmVfbWVhbnMobGFiZWwueSA9IDksIG1ldGhvZCA9ICJhbm92YSIsIHNpemUgPSAyLjUsIGZhbWlseT0ic2FucyIpIysKYGBgCiNDaGVjayB0aGUgdHJlbmQgb2YgTkZLQiBwYXRod2F5IHdpdGggR1JBTUQxQiBzaWduYXR1cmUKCmBgYHtyfQpIQUxMTUFSS19UTkZBX1NJR05BTElOR19WSUFfTkZLQl9nZW5lczwtIGMoJ0pVTkInLCdDWENMMicsJ0FURjMnLCdORktCSUEnLCdUTkZBSVAzJywnUFRHUzInLCdDWENMMScsJ0lFUjMnLCdDRDgzJywnQ0NMMjAnLCdDWENMMycsJ01BRkYnLCdORktCMicsJ1RORkFJUDInLCdIQkVHRicsJ0tMRjYnLCdCSVJDMycsJ1BMQVVSJywnWkZQMzYnLCdJQ0FNMScsJ0pVTicsJ0VHUjMnLCdJTDFCJywnQkNMMkExJywnUFBQMVIxNUEnLCdaQzNIMTJBJywnU09EMicsJ05SNEEyJywnSUwxQScsJ1JFTEInLCdUUkFGMScsJ0JURzInLCdEVVNQMScsJ01BUDNLOCcsJ0VUUzInLCdGMycsJ1NEQzQnLCdFR1IxJywnSUw2JywnVE5GJywnS0RNNkInLCdORktCMScsJ0xJRicsJ1BUWDMnLCdGT1NMMScsJ05SNEExJywnSkFHMScsJ0NDTDQnLCdHQ0gxJywnQ0NMMicsJ1JDQU4xJywnRFVTUDInLCdFSEQxJywnSUVSMicsJ1JFTCcsJ0NGTEFSJywnUklQSzInLCdORktCSUUnLCdOUjRBMycsJ1BITERBMScsJ0lFUjUnLCdUTkZTRjknLCdHRU0nLCdHQURENDVBJywnQ1hDTDEwJywnUExLMicsJ0JITEhFNDAnLCdFR1IyJywnU09DUzMnLCdTTEMyQTYnLCdQVEdFUjQnLCdEVVNQNScsJ1NFUlBJTkIyJywnTkZJTDMnLCdTRVJQSU5FMScsJ1RSSUIxJywnVElQQVJQJywnUkVMQScsJ0JJUkMyJywnQ1hDTDYnLCdMSVRBRicsJ1RORkFJUDYnLCdDRDQ0JywnSU5IQkEnLCdQTEFVJywnTVlDJywnVE5GUlNGOScsJ1NHSzEnLCdUTklQMScsJ05BTVBUJywnRk9TTDInLCdQTlJDMScsJ0lEMicsJ0NENjknLCdJTDdSJywnRUZOQTEnLCdQSExEQTInLCdQRktGQjMnLCdDQ0w1JywnWVJEQycsJ0lGTkdSMicsJ1NRU1RNMScsJ0JURzMnLCdHQURENDVCJywnS1lOVScsJ0cwUzInLCdCVEcxJywnTUNMMScsJ1ZFR0ZBJywnTUFQMkszJywnQ0RLTjFBJywnVEFOSycsJ0lGSVQyJywnSUwxOCcsJ1RVQkIyQScsJ0lSRjEnLCdGT1MnLCdPTFIxJywnUkhPQicsJ0FSRUcnLCdOSU5KMScsJ1pCVEIxMCcsJ1BMUFAzJywnS0xGNCcsJ0NYQ0wxMScsJ1NBVDEnLCdDU0YxJywnR1BSMTgzJywnUE1FUEExJywnUFRQUkUnLCdUTFIyJywnQUNLUjMnLCdLTEYxMCcsJ01BUkNLUycsJ0xBTUIzJywnQ0VCUEInLCdUUklQMTAnLCdGMlJMMScsJ0tMRjknLCdMRExSJywnVEdJRjEnLCdSTkYxOUInLCdEUkFNMScsJ0I0R0FMVDEnLCdETkFKQjQnLCdDU0YyJywnUERFNEInLCdTTk4nLCdQTEVLJywnU1RBVDVBJywnREVOTkQ1QScsJ0NDTkQxJywnRERYNTgnLCdTUEhLMScsJ0NEODAnLCdUTkZBSVA4JywnQ0NOTDEnLCdGVVQ0JywnQ0NSTDInLCdTUFNCMScsJ1RTQzIyRDEnLCdCNEdBTFQ1JywnU0lLMScsJ0NMQ0YxJywnTkZFMkwyJywnRk9TQicsJ1BFUjEnLCdORkFUNScsJ0FUUDJCMScsJ0lMMTJCJywnSUw2U1QnLCdTTEMxNkE2JywnQUJDQTEnLCdIRVMxJywnQkNMNicsJ0lSUzInLCdTTEMyQTMnLCdDRUJQRCcsJ0lMMjNBJywnU01BRDMnLCdUQVAxJywnTVNDJywnSUZJSDEnLCdJTDE1UkEnLCdUTklQMicsJ0JDTDMnLCdQQU5YMScsJ0ZKWDEnLCdFRE4xJywnRUlGMScsJ0JNUDInLCdEVVNQNCcsJ1BETElNNScsJ0lDT1NMRycsJ0dGUFQyJywnS0xGMicsJ1ROQycsJ1NFUlBJTkI4JywnTVhEMScpCmVuc2VtYmxfaWRzPC1yZXMuYW5ub3QkaWRbcmVzLmFubm90JE5hbWUlaW4lIEhBTExNQVJLX1RORkFfU0lHTkFMSU5HX1ZJQV9ORktCX2dlbmVzXQpleHByZXNzaW9uRGF0YUZvckhhbGxtYXJrPC1kYXRhLmZyYW1lKGFzc2F5KHZzZClbZW5zZW1ibF9pZHMsIF0pCiNDYWxjdWxhdGUgYXZlcmFnZSB2YWx1ZSBwZXIgc2FtcGxlCm1lZGlhbkNlbGxUeXBlRXhwcmVzc2lvbiA9IGFwcGx5KHQoZXhwcmVzc2lvbkRhdGFGb3JIYWxsbWFyayksIDEsIG1lZGlhbikKI1JlcGxhY2UgRU5TRU1CTCBJRHMgd2l0aCBjb3JyZXNwb25kaW5nIGdlbmUgbmFtZXMKI25hbWVzKGV4cHJlc3Npb25EYXRhKSA8LSBnZW5lTmFtZQpzdGFuZC5mdW4gPC0gZnVuY3Rpb24oeCl7KHgtbWVhbih4LG5hLnJtPVRSVUUpKS9zZCh4LG5hLnJtPVRSVUUpfQpjbGluaWNhbERhdGEkSEFMTE1BUktfVE5GQV9TSUdOQUxJTkdfVklBX05GS0JfZ2VuZXM8LXN0YW5kLmZ1bihtZWRpYW5DZWxsVHlwZUV4cHJlc3Npb24pW3Jvd25hbWVzKGNsaW5pY2FsRGF0YSldCmNsaW5pY2FsRGF0YSRIQUxMTUFSS19UTkZBX1NJR05BTElOR19WSUFfTkZLQl9nZW5lc19ncm91cHM8LWlmZWxzZShjbGluaWNhbERhdGEkSEFMTE1BUktfVE5GQV9TSUdOQUxJTkdfVklBX05GS0JfZ2VuZXMgPj0gbWVkaWFuKGNsaW5pY2FsRGF0YSRIQUxMTUFSS19UTkZBX1NJR05BTElOR19WSUFfTkZLQl9nZW5lcyksICJIaWdoIiwgIkxvdyIpCiNwbG90IHRoZSByZXN1bHRzIApnZ3Bsb3QoY2xpbmljYWxEYXRhLCBhZXMoeD1TaWduYXR1cmVHcm91cE1lYW4sIHk9SEFMTE1BUktfVE5GQV9TSUdOQUxJTkdfVklBX05GS0JfZ2VuZXMpKSsKICBnZW9tX3Zpb2xpbigpKwogIGdlb21faml0dGVyKGhlaWdodCA9IDAsIHdpZHRoID0gMC4xLCBhbHBoYSA9IDAuNSwgYWVzKCkpKwogICNzY2FsZV9maWxsX2JyZXdlcih0eXBlPSJxdWFsIiwgcGFsZXR0ZSA9ICJEYXJrMiIsIG5hbWUgPSAiQ2FtXzEyMSByaXNrIGdyb3VwIikrCiAgeGxhYigiR1JBTUQxQiBncm91cHMiKSsKICB5bGFiKCJIQUxMTUFSS19UTkZBX1NJR05BTElOR19WSUFfTkZLQlxuKFN0YW5kLiBtZWRpYW4gVlNUIG5vcm1hbGl6ZWQgZ2VuZSBleHByZXNzaW9uKSIpKwogICNnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBxdWFudGlsZShjbGluaWNhbERhdGEkU2lnbmF0dXJlLCAwLjc1KSwgY29sb3IgPSAiZ3JleSIsIGxpbmV0eXBlID0gImxvbmdkYXNoIikrCiAgdGhlbWUodGV4dD1lbGVtZW50X3RleHQoc2l6ZT03LCAgZmFtaWx5PSJzYW5zIikpKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGxpbmV0eXBlPSJkYXNoZWQiKSsKICBnZ3B1YnI6OnN0YXRfY29tcGFyZV9tZWFucyhtZXRob2QgPSAidC50ZXN0Iiwgc2l6ZSA9IDIuNSwgZmFtaWx5PSJzYW5zIikjKyAjIEFkZCBwYWlyd2lzZSBjb21wYXJpc29ucyBwLXZhbHVlCiAgI2dncHVicjo6c3RhdF9jb21wYXJlX21lYW5zKGxhYmVsLnkgPSA5LCBtZXRob2QgPSAiYW5vdmEiLCBzaXplID0gMi41LCBmYW1pbHk9InNhbnMiKSMrCmBgYApgYGB7cn0KI3Bsb3QgdGhlIGNvcnJlbGF0aW9uIGxpbmVzCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpsaWJyYXJ5KHJvYnVzdGJhc2UpCmRmID0gZGF0YS5mcmFtZSgiR1JBTUQxQiIgPSBjbGluaWNhbERhdGEkU2lnbmF0dXJlLAogICAgICAgICAgICAgICAgIkhBTExNQVJLIiA9IGNsaW5pY2FsRGF0YSRIQUxMTUFSS19UTkZBX1NJR05BTElOR19WSUFfTkZLQl9nZW5lcywKICAgICAgICAgICAgICAgICJHUkFNRDFCX2dyb3VwcyIgPSBjbGluaWNhbERhdGEkU2lnbmF0dXJlR3JvdXBNZWFuLAogICAgICAgICAgICAgICAgIkhBTExNQVJLX2dyb3VwcyI9Y2xpbmljYWxEYXRhJEhBTExNQVJLX1RORkFfU0lHTkFMSU5HX1ZJQV9ORktCX2dlbmVzX2dyb3VwcykKCnBjYVJlc3VsdHMgPC0gcHJjb21wKHQoZXhwcmVzc2lvbkRhdGFGb3JIYWxsbWFyaykpCnBlcmNlbnRWYXIgPSByb3VuZCgxMDAqcGNhUmVzdWx0cyRzZGV2XjIvc3VtKHBjYVJlc3VsdHMkc2Rldl4yKSwxKQoKcGRmKGZpbGU9In4vRGVza3RvcC9NZWxhbm9tYS9TaHJ1dGh5L0dSQU1EMUIvcmVzdWx0cy9ORktCX0dSQU1EMUJfZ3JvdXBzX3NjcmVlcGxvdC5wZGYiKQpwbG90MiA9IHNjcmVlcGxvdChwY2FSZXN1bHRzLCB0eXBlID0gJ2xpbmVzJykKZGV2Lm9mZigpCgpmb3IgKHhDb21wb25lbnQgaW4gMTozKXsKICAgIGZvciAoeUNvbXBvbmVudCBpbiAoeENvbXBvbmVudCsxKTo0KXsKICAgICAgZGYkeEF4aXM8LXBjYVJlc3VsdHMkeFsseENvbXBvbmVudF0KICAgICAgZGYkeUF4aXM8LXBjYVJlc3VsdHMkeFsseUNvbXBvbmVudF0KICAgICAgZzc8LWdncGxvdChkZiwgYWVzKHg9eEF4aXMsIHk9eUF4aXMsIGNvbG9yPUdSQU1EMUJfZ3JvdXBzLCBzaGFwZSA9IEhBTExNQVJLX2dyb3VwcykpKwogICAgICAgIGdlb21fcG9pbnQoYWxwaGEgPSAwLjUpKwogICAgICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCIjRTMxQTFDIiwgIiMxRjc4QjQiKSwgbmFtZSA9ICJHUkFNRDFCXG5ncm91cHMiKSsKICAgICAgICBzY2FsZV9zaGFwZShuYW1lPSJIQUxMTUFSS1xuZ3JvdXBzIikrCiAgICAgICAgY29vcmRfZml4ZWQoKSsKICAgICAgICBsYWJzKHggPSBwYXN0ZTAoIlBDIix4Q29tcG9uZW50LCIgKCIsIHJvdW5kKHBlcmNlbnRWYXJbeENvbXBvbmVudF0sNCksIiUpIiksCiAgICAgICAgICAgICB5ID0gcGFzdGUwKCJQQyIseUNvbXBvbmVudCwiICgiLCByb3VuZChwZXJjZW50VmFyW3lDb21wb25lbnRdLDQpLCIlKSIpKSArCiAgICAgICAgdGhlbWUodGV4dD1lbGVtZW50X3RleHQoc2l6ZT03LCAgZmFtaWx5PSJzYW5zIiksIGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpCiAgICAgIGdnc2F2ZShwYXN0ZTAoIn4vRGVza3RvcC9NZWxhbm9tYS9TaHJ1dGh5L0dSQU1EMUIvcmVzdWx0cy9ORktCX0dSQU1EMUJfZ3JvdXBzX1BDQV9QQyIseENvbXBvbmVudCwiX3ZzX1BDIix5Q29tcG9uZW50LCIucGRmIiwgY29sbGFwc2UgPSAiIiksIGRldmljZSA9ICJwZGYiLCB1bml0cyA9ICJjbSIsIHdpZHRoID0xNCwgaGVpZ2h0PTYpCiAgICB9fQogIApnMTwtIGdncGxvdChkZiwgYWVzKHg9R1JBTUQxQiwgeT1IQUxMTUFSSywgY29sb3I9R1JBTUQxQl9ncm91cHMpKSsKICAgICBnZW9tX3BvaW50KGFscGhhID0gMC41KSsKICAgICB4bGFiKCJWU1Qgbm9ybWFsaXplZCBHUkFNRDFCIGV4cHJlc3Npb24gKHN0YW5kLikiKSsKICAgICB5bGFiKCJIQUxMTUFSS19UTkZBX1NJR05BTElOR19WSUFfTkZLQlxuKFN0YW5kLiBtZWRpYW4gVlNUIG5vcm1hbGl6ZWQgZ2VuZSBleHByZXNzaW9uKSIpKwogICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCIjMUY3OEI0IiwgIiNFMzFBMUMiKSwgbmFtZSA9ICJHUkFNRDFCXG5ncm91cHMiKSsKICAgICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBhbHBoYSA9IC4xNSwgYWVzKGZpbGwgPSBHUkFNRDFCX2dyb3VwcykpKwogICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIiMxRjc4QjQiLCAiI0UzMUExQyIpLCBuYW1lID0gIkdSQU1EMUJcbmdyb3VwcyIpKwogIHRoZW1lKHRleHQ9ZWxlbWVudF90ZXh0KHNpemU9NywgIGZhbWlseT0ic2FucyIpLCBsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKSsKICBnZ3B1YnI6OnN0YXRfY29yKGFlcyhjb2xvciA9IEdSQU1EMUJfZ3JvdXBzKSwgbGFiZWwueCA9IC0wLjc1LCBsYWJlbC55ID1jKG1heChkZiRIQUxMTUFSSykrMC40NSwgbWF4KGRmJEhBTExNQVJLKSswLjEpLCBzaXplID0gMi41LCBmYW1pbHk9InNhbnMiKSsKICB5bGltKGMobWluKGRmJEhBTExNQVJLKSwgbWF4KGRmJEhBTExNQVJLKSswLjUpKSsKICBjb29yZF9maXhlZCgpCmdnc2F2ZSgifi9EZXNrdG9wL01lbGFub21hL1NocnV0aHkvR1JBTUQxQi9yZXN1bHRzL05GS0JfR1JBTUQxQl9ncm91cHMucGRmIiwgZGV2aWNlID0gInBkZiIsIHVuaXRzID0gImNtIiwgd2lkdGggPSA4LCBoZWlnaHQ9OCkKCmRmJFBDMTwtcGNhUmVzdWx0cyR4WywxXQpkZiRQQzI8LXBjYVJlc3VsdHMkeFssMl0KZGYkUEMzPC1wY2FSZXN1bHRzJHhbLDNdCgpwbG90X2x5KHg9ZGYkUEMxLCB5PWRmJFBDMiwgej1kZiRQQzMsIHR5cGU9InNjYXR0ZXIzZCIsIG1vZGU9Im1hcmtlcnMiLCBjb2xvcj1kZiRHUkFNRDFCX2dyb3Vwcywgc3ltYm9scyA9ZGYkSEFMTE1BUktfZ3JvdXBzKQoKYGBgCgojUGVyZm9ybSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcwpgYGB7cn0KY29tYmluZWRTY29yZSRDbGFya1Njb3JlIDwtIGFzLmZhY3Rvcihjb21iaW5lZFNjb3JlJENsYXJrU2NvcmUpCmxldmVscyhjb21iaW5lZFNjb3JlJENsYXJrU2NvcmUpIDwtIGMoImFic2VudCIsICJub25icmlzayIsICJicmlzayIpCmNsaW5pY2FsRGF0YSRDbGFya1Njb3JlPC1jb21iaW5lZFNjb3JlW3Jvd25hbWVzKGNsaW5pY2FsRGF0YSksICJDbGFya1Njb3JlIl0KI21lcmdlKGNsaW5pY2FsRGF0YSwgY29tYmluZWRTY29yZSwgYnkueD0iUk5BLlNlcS5TYW1wbGUiLCBieS55PSJSLlNlcV9zYW1wbGVJRCIpCiNyb3duYW1lcyhjbGluaWNhbERhdGEpPC1jbGluaWNhbERhdGEkUk5BLlNlcS5TYW1wbGUKI2NsaW5pY2FsRGF0YSRDbGFya1Njb3JlPWNvbWJpbmVkU2NvcmUkQ2xhcmtTY29yZVtyb3duYW1lcyhjbGluaWNhbERhdGEpXQpgYGAKCmBgYHtyfQojV2hpbGUgYWNjb3VudGluZyBmb3IgU3RhZ2UsIE5SQVMgbXV0YXRpb24sIFRJTCBzY29yZSBhbmQgRXZlbnRNZXQKI1N1YnNldCB0byBvbmx5IHRob3NlIHNhbXBsZXMgd2hpY2ggaGF2ZSBub24tbWlzc2luZyBlbnRpcmVzIGZvciB0aGVzZSBjb3ZhcmlhdGVzCmNsaW5pY2FsRGF0YV9zdWI8LWNsaW5pY2FsRGF0YVsoIWlzLm5hKGNsaW5pY2FsRGF0YSRTdGFnZSkpJighaXMubmEoY2xpbmljYWxEYXRhJE5SQVMpKSYoIWlzLm5hKGNsaW5pY2FsRGF0YSRDbGFya1Njb3JlKSkmKCFpcy5uYShjbGluaWNhbERhdGEkRXZlbnRNZXQpKSwgXQpkYXRhLmZfc3ViPC1kYXRhLmZbLCByb3duYW1lcyhjbGluaWNhbERhdGFfc3ViKV0KYGBgCgpgYGB7cn0KdGFibGUoY2xpbmljYWxEYXRhX3N1YiRTaWduYXR1cmVHcm91cE1lYW4pCmBgYAoKYGBge3J9CmRkczEgPC0gREVTZXFEYXRhU2V0RnJvbU1hdHJpeChjb3VudERhdGEgPSBkYXRhLmZfc3ViLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sRGF0YSAgID0gY2xpbmljYWxEYXRhX3N1YiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2lnbiAgICA9IH4gU3RhZ2UrTlJBUytDbGFya1Njb3JlK0V2ZW50TWV0K1NpZ25hdHVyZUdyb3VwTWVhbikKYGBgCgpgYGB7cn0KdG90YWxjb3VudHMxLmdlbmUgPSByb3dTdW1zKGNvdW50cyhkZHMxKSkKZGRzMSA8LSBkZHMxW3RvdGFsY291bnRzMS5nZW5lPj0xMCxdCmBgYAoKYGBge3J9CiMgZGltZW5zaW9ucyBwb3N0IGZpbHRlcmluZwptID0gbnJvdyhkZHMxKQpuID0gbmNvbChkZHMxKQpgYGAKCmBgYHtyfQpkZHMxIDwtIERFU2VxKGRkczEpCnJlczEgPC0gcmVzdWx0cyhkZHMxKQpyZXMxCmBgYApgYGB7cn0KI01lcmdlIHdpdGggZ2VuZSBuYW1lcyBhbmQgY2hlY2sgaWYgdGhlIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyBtYWtlIHNlbnNlCnJlczwtZGF0YS5mcmFtZShyZXMxKQpyZXM8LXJlc1tvcmRlcihyZXMkcGFkaiksIF0KcmVzJE5hbWU8LXJlcy5hbm5vdFtyb3duYW1lcyhyZXMpLCAiTmFtZSJdCmhlYWQocmVzKQojR1JBTUQxQiBpcyBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgaW4gR1JBTUQxQiBncm91cHMgYW5kIGlzIGRvd25yZWd1bGF0ZWQgaW4gdGhlIGxvdyBleHByZXNzaW9uIGdyb3VwIGNvbXBhcmVkIHRvIHRoZSBoaWdoLWV4cHJlc3Npb24gZ3JvdXAgc28gYXRsZWFzdCB0aGlzIG1ha2VzIHNlbnNlLgpgYGAKI1BlcmZvcm0gbGZjIHNocmlua2FnZSBmb3IgR1NFQQpgYGB7cn0KbGlicmFyeSgiYXBlZ2xtIikKY29lZk5hbWUgPSByZXN1bHRzTmFtZXMoZGRzMSkKcmVzV2l0aExmY1Nocmlua2FnZSA9IGRhdGEuZnJhbWUobGZjU2hyaW5rKGRkczEsIGNvZWYgPSB0YWlsKGNvZWZOYW1lLCBuPTEpLCB0eXBlPSJhcGVnbG0iKSkKI0FkZCBzdWZmaXggdG8gY29sbmFtZXMgZm9yIGVhc3kgaWRlbnRpZmljYXRpb24gbGF0ZXIgb24uCmNvbG5hbWVzKHJlc1dpdGhMZmNTaHJpbmthZ2UpID0gcGFzdGUoY29sbmFtZXMocmVzV2l0aExmY1Nocmlua2FnZSksImxmY1Nocmlua0FwcGxpZWQiLCBzZXAgPSAiXyIpCiNBZGQgRU5TRU1CTCBJRCBhcyBhIGNvbHVtbiBpbiB0aGUgZGF0YSBmcmFtZSB0byBlYXNlIG1lcmdpbmcgbGF0ZXIKcmVzV2l0aExmY1Nocmlua2FnZSRpZCA9IHJvd25hbWVzKHJlc1dpdGhMZmNTaHJpbmthZ2UpCmBgYAoKYGBge3J9CiNNZXJnZSB0aGVzZSB3aXRoIERFIG9yaWdpbmFsIERFIHJlc3VsdHMKcmVzJGlkPC1yb3duYW1lcyhyZXMpCmRlUmVzdWx0c1VwZGF0ZWQgPSBtZXJnZSh4ID0gcmVzLCB5ID0gZGF0YS5mcmFtZShyZXNXaXRoTGZjU2hyaW5rYWdlKSwgYnkgPSAiaWQiKQpkZVJlc3VsdHNVcGRhdGVkID0gZGVSZXN1bHRzVXBkYXRlZFtvcmRlcihkZVJlc3VsdHNVcGRhdGVkJHBhZGopLF0KI1NhdmUgdGhlIHJlc3VsdHMKd3JpdGUudGFibGUoZGVSZXN1bHRzVXBkYXRlZCwgZmlsZSA9ICIuLi9yZXN1bHRzL2RlX2dzZWEvZGVXaXRoTGZjU2tyaW5rYWdlQXBwbGllZF9HUkFNRDFCX2FsbC50c3YiLCBjb2wubmFtZXMgPSBUUlVFLCByb3cubmFtZXMgPSBGQUxTRSwgcXVvdGUgPSBGQUxTRSwgc2VwID0gJ1x0JykKI1NhdmUgdGhlIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyAocGFkajwwLjEpCndyaXRlLnRhYmxlKGRlUmVzdWx0c1VwZGF0ZWRbZGVSZXN1bHRzVXBkYXRlZCRwYWRqX2xmY1Nocmlua0FwcGxpZWQ8MC4xLCBdLCBmaWxlID0gIi4uL3Jlc3VsdHMvZGVfZ3NlYS9kZVdpdGhMZmNTa3JpbmthZ2VBcHBsaWVkX0dSQU1EMUJfcGFkal8wXzEudHN2IiwgY29sLm5hbWVzID0gVFJVRSwgcm93Lm5hbWVzID0gRkFMU0UsIHF1b3RlID0gRkFMU0UsIHNlcCA9ICdcdCcpCmBgYAoKYGBge3J9CmhlYWQoZGVSZXN1bHRzVXBkYXRlZCkKYGBgCgojQ3JlYXRlIGEgcmFua2VkIGxpc3QgZm9yIEdTRUEKYGBge3J9CiNDcmVhdGUgcmFua2VkIGxpc3QgZm9yIHJ1bm5pbmcgUHJlLXJhbmtlZCBHU0VBIHRvb2wKZGVSZXN1bHRzVXBkYXRlZDwtcmVhZC50YWJsZSgiLi4vcmVzdWx0cy9kZV9nc2VhL2RlV2l0aExmY1Nrcmlua2FnZUFwcGxpZWRfR1JBTUQxQl9hbGwudHN2IiwgaGVhZGVyID0gVCwgc2VwID0gIlx0IiwgcXVvdGUgPSAiIikKcmFua2VkTGlzdCA9IG5hLm9taXQoZGF0YS5mcmFtZShkZVJlc3VsdHNVcGRhdGVkJE5hbWUsIGRlUmVzdWx0c1VwZGF0ZWQkbG9nMkZvbGRDaGFuZ2VfbGZjU2hyaW5rQXBwbGllZCkpCnJhbmtlZExpc3QgPSByYW5rZWRMaXN0W3dpdGgocmFua2VkTGlzdCwgb3JkZXIoZGVSZXN1bHRzVXBkYXRlZC5sb2cyRm9sZENoYW5nZV9sZmNTaHJpbmtBcHBsaWVkLCBkZWNyZWFzaW5nID0gVCkpLCBdCiNTYXZlIHRoZSByYW5rZWQgbGlzdCB0byBhIG5ldyBmaWxlIGZvciBHU0VBLgp3cml0ZS50YWJsZShyYW5rZWRMaXN0LCBmaWxlID0gIi4uL3Jlc3VsdHMvZGVfZ3NlYS9yYW5rZWRMaXN0X0dSQU1EMUIucm5rIiwgY29sLm5hbWVzID0gRkFMU0UsIHJvdy5uYW1lcyA9IEZBTFNFLCBxdW90ZSA9IEZBTFNFLCBzZXAgPSAnXHQnKQpgYGAKCiNQbG90IHN1cnZpdmFsIGN1cnZlcyBiZXR3ZWVuIHRoZSBjb250aW51b3VzIHNpZ25hdHVyZSBhbmQgdGhlIHR3byBncm91cHMKIyMgRmlyc3QgYWRkIG5lY2Vzc3NhcnkgY29sdW1ucwpgYGB7cn0Kc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKGxpYnJhcnkoInN1cnZpdmFsIikpCiMgc3Vydml2YWwgb3V0Y29tZSAxOiAiZCIgZm9yIGRlYXRoIGFuZCAibHRyYyIgZm9yIGxlZnQgdHJ1bmNhdGVkIGFuZCByaWdodCBjZW5zb3JlZApjbGluaWNhbERhdGEkc3Vydml2YWxfZF9sdHJjID0gU3Vydih0aW1lICA9IGFzLm51bWVyaWMoZGlmZnRpbWUoY2xpbmljYWxEYXRhJERPRSwgY2xpbmljYWxEYXRhJEREaWFnKSkvMzY1LjI1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aW1lMiA9IGFzLm51bWVyaWMoZGlmZnRpbWUoY2xpbmljYWxEYXRhJERPQywgY2xpbmljYWxEYXRhJEREaWFnKSkvMzY1LjI1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBldmVudCA9IGlmZWxzZShjbGluaWNhbERhdGEkRGVhZCA9PSBGQUxTRSwgMCwgMSkpCgojIHN1cnZpdmFsIG91dGNvbWUgMjogInJkIiBmb3IgcmVsYXBzZSBvciBkZWF0aCwgImx0cmMiIGZvciBsZWZ0IHRydW5jYXRlZCBhbmQgcmlnaHQgY2Vuc29yZWQKY2xpbmljYWxEYXRhJHN1cnZpdmFsX3JkX2x0cmMgPSBTdXJ2KHRpbWUgID0gYXMubnVtZXJpYyhkaWZmdGltZShjbGluaWNhbERhdGEkRE9FLCBjbGluaWNhbERhdGEkRERpYWcpKS8zNjUuMjUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aW1lMiA9IGFzLm51bWVyaWMoZGlmZnRpbWUoYXBwbHkoY2xpbmljYWxEYXRhWywgYygiRE9DIiwgIkREaXN0TWV0cyIpXSwxLG1pbixuYS5ybT1UUlVFKSwgY2xpbmljYWxEYXRhJEREaWFnKSkvMzY1LjI1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXZlbnQgPSBpZmVsc2UoKGNsaW5pY2FsRGF0YSREZWFkID09IEZBTFNFICYgY2xpbmljYWxEYXRhJEV2ZW50TWV0ID09ICJObyIpLCAwLCAxKSkKCmlmKHRpc3N1ZT09IlNraW4iKXsKICBqdF9uc19zbV9zY29yZXMgPSByZWFkLnRhYmxlKCIuLi9KVF9OU19TTV9Db21iaW5lZC54bHMgLSBKVF9OU19TTS50c3YiLCBzZXAgPSAiXHQiLCBoZWFkZXIgPSBUUlVFLCBxdW90ZSA9ICIiKQogIGNvbWJpbmVkU2NvcmUgPC0gZGF0YS5mcmFtZSgiU2Nhbm5lZF9maWxlLm1lIj0ganRfbnNfc21fc2NvcmVzJEpUX1NjYW5uZWRfZmlsZS5tZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJSLlNlcV9zYW1wbGVJRCI9IGp0X25zX3NtX3Njb3JlcyRKVF9SLlNlcV9zYW1wbGVJRCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDbGFya1Njb3JlIj1pZmVsc2UoanRfbnNfc21fc2NvcmVzJEpUX0NsYXJrX3Njb3JlPT1qdF9uc19zbV9zY29yZXMkTlNfQ2xhcmtfc2NvcmUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAganRfbnNfc21fc2NvcmVzJEpUX0NsYXJrX3Njb3JlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBqdF9uc19zbV9zY29yZXMkU01fQ2xhcmtfc2NvcmUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiTUlBU2NvcmUiPSBpZmVsc2UoanRfbnNfc21fc2NvcmVzJEpUX1RJTF9ncmFkZT09anRfbnNfc21fc2NvcmVzJE5TX1RJTC5HUkFERSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGp0X25zX3NtX3Njb3JlcyRKVF9USUxfZ3JhZGUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAganRfbnNfc21fc2NvcmVzJFNNX1RJTF9ncmFkZSkpCiAgI1JlbW92ZSBkdXBsaWNhdGVkIGVudHJpZXMKICBjb21iaW5lZFNjb3JlPC1jb21iaW5lZFNjb3JlWyFkdXBsaWNhdGVkKGNvbWJpbmVkU2NvcmUkUi5TZXFfc2FtcGxlSUQpLCBdCiAgcm93bmFtZXMoY29tYmluZWRTY29yZSk8LWNvbWJpbmVkU2NvcmUkUi5TZXFfc2FtcGxlSUQgI3JlbmFtZSByb3duYW1lcyB3aXRoIHNhbXBsZSBpZHMKICBjb21iaW5lZFNjb3JlJENsYXJrU2NvcmUgPC0gYXMuZmFjdG9yKGNvbWJpbmVkU2NvcmUkQ2xhcmtTY29yZSkKICBsZXZlbHMoY29tYmluZWRTY29yZSRDbGFya1Njb3JlKSA8LSBjKCJhYnNlbnQiLCAibm9uYnJpc2siLCAiYnJpc2siKQogIGNsaW5pY2FsRGF0YTwtbWVyZ2UoY2xpbmljYWxEYXRhLCBjb21iaW5lZFNjb3JlLCBieS54PSJSTkEuU2VxLlNhbXBsZSIsIGJ5Lnk9IlIuU2VxX3NhbXBsZUlEIikKICByb3duYW1lcyhjbGluaWNhbERhdGEpPC1jbGluaWNhbERhdGEkUk5BLlNlcS5TYW1wbGUKfQpgYGAKCiMjIHRoZW4gY2FsY3VsYXRlIHRoZSB2YWx1ZXMKYGBge3J9Cm9zPC1kYXRhLmZyYW1lKCkKcGZzPC1kYXRhLmZyYW1lKCkKdGVtcDwtZGF0YS5mcmFtZSgpCgpjb21iaW5hdGlvbnM8LWMoIlNpZ25hdHVyZSIsICJTaWduYXR1cmVHcm91cE1lYW4iLCAiU2lnbmF0dXJlR3JvdXBNZWRpYW4iLCAiU2lnbmF0dXJlR3JvdXAwXzMzIiwgIlNpZ25hdHVyZUdyb3VwMF82NiIsICJTaWduYXR1cmVHcm91cERlbnNpdHlJbnRlcnNlY3Rpb24iKQoKZm9yIChjb21iaW5hdGlvbiBpbiBjb21iaW5hdGlvbnMpIHsKICBpZiAodGlzc3VlPT0iU2tpbiIpIHsKICAgIG9zX2Zvcm11bGE8LXBhc3RlMCgic3Vydml2YWxfZF9sdHJjfiIsY29tYmluYXRpb24sIitTZXgrQWdlK2FzLm51bWVyaWMoTmNsYXNzKSthcy5jaGFyYWN0ZXIoU3RhZ2UpK0VDT0crdHJlYXRtZW50K0NsYXJrU2NvcmUiKQogICAgcGZzX2Zvcm11bGE8LXBhc3RlMCgic3Vydml2YWxfcmRfbHRyY34iLGNvbWJpbmF0aW9uLCIrU2V4K0FnZSthcy5udW1lcmljKE5jbGFzcykrYXMuY2hhcmFjdGVyKFN0YWdlKStFQ09HK3RyZWF0bWVudCtDbGFya1Njb3JlIikKICB9IGVsc2V7CiAgICBvc19mb3JtdWxhPC1wYXN0ZTAoInN1cnZpdmFsX2RfbHRyY34iLGNvbWJpbmF0aW9uLCIrU2V4K0FnZSthcy5udW1lcmljKE5jbGFzcykrYXMuY2hhcmFjdGVyKFN0YWdlKStFQ09HK3RyZWF0bWVudCIpCiAgICBwZnNfZm9ybXVsYTwtcGFzdGUwKCJzdXJ2aXZhbF9yZF9sdHJjfiIsY29tYmluYXRpb24sIitTZXgrQWdlK2FzLm51bWVyaWMoTmNsYXNzKSthcy5jaGFyYWN0ZXIoU3RhZ2UpK0VDT0crdHJlYXRtZW50IikKICB9CiAgCiAgZml0ID0gY29lZihzdW1tYXJ5KGNveHBoKGFzLmZvcm11bGEob3NfZm9ybXVsYSksIGRhdGE9Y2xpbmljYWxEYXRhKSkpCiAgbWlkICA9IGZpdFsxLGMoImV4cChjb2VmKSIpXQogIGxvdyAgPSBleHAoZml0WzEsYygiY29lZiIpXS0KICAgICAgICAgICAgICBxbm9ybSguOTc1KSpmaXRbMSxjKCJzZShjb2VmKSIpXSkgICAgICAgICAgICAgICAgICAgCiAgaGlnaCA9IGV4cChmaXRbMSxjKCJjb2VmIildKwogICAgICAgICAgICAgIHFub3JtKC45NzUpKmZpdFsxLGMoInNlKGNvZWYpIildKSAgCiAgcHZhbCA9IGZpdFsxLGMoIlByKD58enwpIildCiAgdGVtcFsiU2lnbmF0dXJlIixjKCJTaWduYXR1cmUiLCAiSFIiLCJsb3ciLCJoaWdoIiwicHZhbCIpXSA9IGMocm93bmFtZXMoZml0KVsxXSxtaWQsbG93LGhpZ2gscHZhbCkKICBvczwtcmJpbmQob3MsIHRlbXApCiAgCiAgZml0ID0gY29lZihzdW1tYXJ5KGNveHBoKGFzLmZvcm11bGEocGZzX2Zvcm11bGEpLCBkYXRhPWNsaW5pY2FsRGF0YSkpKQogIG1pZCAgPSBmaXRbMSxjKCJleHAoY29lZikiKV0KICBsb3cgID0gZXhwKGZpdFsxLGMoImNvZWYiKV0tCiAgICAgICAgICAgICAgcW5vcm0oLjk3NSkqZml0WzEsYygic2UoY29lZikiKV0pICAgICAgICAgICAgICAgICAgIAogIGhpZ2ggPSBleHAoZml0WzEsYygiY29lZiIpXSsKICAgICAgICAgICAgICBxbm9ybSguOTc1KSpmaXRbMSxjKCJzZShjb2VmKSIpXSkgIAogIHB2YWwgPSBmaXRbMSxjKCJQcig+fHp8KSIpXQogIHRlbXBbIlNpZ25hdHVyZSIsYygiU2lnbmF0dXJlIiwgIkhSIiwibG93IiwiaGlnaCIsInB2YWwiKV0gPSBjKHJvd25hbWVzKGZpdClbMV0sbWlkLGxvdyxoaWdoLHB2YWwpCiAgcGZzPC1yYmluZChwZnMsIHRlbXApCn0KCndyaXRlLnRhYmxlKG9zLCBwYXN0ZSgiLi4vcmVzdWx0cy9zdXJ2aXZhbC9BVkFTVC1NXyIsdGlzc3VlLCJfb3MudHN2Iiwgc2VwPSIiKSwgc2VwPSJcdCIsIGNvbC5uYW1lcyA9IFQsIHJvdy5uYW1lcyA9IEYpCndyaXRlLnRhYmxlKHBmcywgcGFzdGUoIi4uL3Jlc3VsdHMvc3Vydml2YWwvQVZBU1QtTV8iLHRpc3N1ZSwiX3Bmcy50c3YiLCBzZXA9IiIpLCBzZXA9Ilx0IiwgY29sLm5hbWVzID0gVCwgcm93Lm5hbWVzID0gRikKYGBgCg==